updates.
[silc.git] / apps / silcd / command.c
index e45e75af89aa670c05463896016d673d79855aec..653996dccfbee45aaca632af43710daf34a1d027 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
@@ -29,14 +29,16 @@ static int silc_server_is_registered(SilcServer server,
 static void 
 silc_server_command_send_status_reply(SilcServerCommandContext cmd,
                                      SilcCommand command,
-                                     SilcCommandStatus status);
+                                     SilcStatus status,
+                                     SilcStatus error);
 static void 
 silc_server_command_send_status_data(SilcServerCommandContext cmd,
                                     SilcCommand command,
-                                    SilcCommandStatus status,
-                                    uint32 arg_type,
-                                    unsigned char *arg,
-                                    uint32 arg_len);
+                                    SilcStatus status,
+                                    SilcStatus error,
+                                    SilcUInt32 arg_type,
+                                    const unsigned char *arg,
+                                    SilcUInt32 arg_len);
 static bool
 silc_server_command_pending_error_check(SilcServerCommandContext cmd,
                                        SilcServerCommandReplyContext cmdr,
@@ -56,8 +58,7 @@ SilcServerCommand silc_command_list[] =
   SILC_SERVER_CMD(quit, QUIT, SILC_CF_LAG | SILC_CF_REG),
   SILC_SERVER_CMD(kill, KILL, SILC_CF_LAG_STRICT | SILC_CF_REG | SILC_CF_OPER),
   SILC_SERVER_CMD(info, INFO, SILC_CF_LAG | SILC_CF_REG),
-  SILC_SERVER_CMD(connect, CONNECT, 
-                 SILC_CF_LAG | SILC_CF_REG | SILC_CF_OPER),
+  SILC_SERVER_CMD(stats, STATS, SILC_CF_LAG | SILC_CF_REG),
   SILC_SERVER_CMD(ping, PING, SILC_CF_LAG | SILC_CF_REG),
   SILC_SERVER_CMD(oper, OPER, SILC_CF_LAG | SILC_CF_REG | SILC_CF_OPER),
   SILC_SERVER_CMD(join, JOIN, SILC_CF_LAG_STRICT | SILC_CF_REG),
@@ -67,16 +68,21 @@ SilcServerCommand silc_command_list[] =
   SILC_SERVER_CMD(cumode, CUMODE, SILC_CF_LAG | SILC_CF_REG),
   SILC_SERVER_CMD(kick, KICK, SILC_CF_LAG_STRICT | SILC_CF_REG),
   SILC_SERVER_CMD(ban, BAN, SILC_CF_LAG_STRICT | SILC_CF_REG),
-  SILC_SERVER_CMD(close, CLOSE,
-                 SILC_CF_LAG | SILC_CF_REG | SILC_CF_OPER),
-  SILC_SERVER_CMD(shutdown, SHUTDOWN, SILC_CF_LAG | SILC_CF_REG | 
-                 SILC_CF_OPER),
+  SILC_SERVER_CMD(detach, DETACH, SILC_CF_LAG_STRICT | SILC_CF_REG),
+  SILC_SERVER_CMD(watch, WATCH, SILC_CF_LAG | SILC_CF_REG),
   SILC_SERVER_CMD(silcoper, SILCOPER,
                  SILC_CF_LAG | SILC_CF_REG | SILC_CF_SILC_OPER),
   SILC_SERVER_CMD(leave, LEAVE, SILC_CF_LAG_STRICT | SILC_CF_REG),
   SILC_SERVER_CMD(users, USERS, SILC_CF_LAG | SILC_CF_REG),
   SILC_SERVER_CMD(getkey, GETKEY, SILC_CF_LAG | SILC_CF_REG),
 
+  SILC_SERVER_CMD(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 },
 };
 
@@ -87,30 +93,32 @@ SilcServerCommand silc_command_list[] =
 
    It also checks that the requested command includes correct amount
    of arguments. */
-#define SILC_SERVER_COMMAND_CHECK(command, context, min, max)                \
-do {                                                                         \
-  uint32 _argc;                                                                      \
-                                                                             \
-  SILC_LOG_DEBUG(("Start"));                                                 \
-                                                                             \
-  if (silc_server_command_pending_error_check(cmd, context2, command)) {      \
-    silc_server_command_free(cmd);                                           \
-    return;                                                                  \
-  }                                                                          \
-                                                                             \
-  _argc = silc_argument_get_arg_num(cmd->args);                                      \
-  if (_argc < min) {                                                         \
-    silc_server_command_send_status_reply(cmd, command,                              \
-                                         SILC_STATUS_ERR_NOT_ENOUGH_PARAMS); \
-    silc_server_command_free(cmd);                                           \
-    return;                                                                  \
-  }                                                                          \
-  if (_argc > max) {                                                         \
-    silc_server_command_send_status_reply(cmd, command,                              \
-                                         SILC_STATUS_ERR_TOO_MANY_PARAMS);   \
-    silc_server_command_free(cmd);                                           \
-    return;                                                                  \
-  }                                                                          \
+#define SILC_SERVER_COMMAND_CHECK(command, context, min, max)               \
+do {                                                                        \
+  SilcUInt32 _argc;                                                         \
+                                                                            \
+  SILC_LOG_DEBUG(("Start"));                                                \
+                                                                            \
+  if (silc_server_command_pending_error_check(cmd, context2, command)) {     \
+    silc_server_command_free(cmd);                                          \
+    return;                                                                 \
+  }                                                                         \
+                                                                            \
+  _argc = silc_argument_get_arg_num(cmd->args);                                     \
+  if (_argc < min) {                                                        \
+    silc_server_command_send_status_reply(cmd, command,                             \
+                                         SILC_STATUS_ERR_NOT_ENOUGH_PARAMS, \
+                                         0);                                \
+    silc_server_command_free(cmd);                                          \
+    return;                                                                 \
+  }                                                                         \
+  if (_argc > max) {                                                        \
+    silc_server_command_send_status_reply(cmd, command,                             \
+                                         SILC_STATUS_ERR_TOO_MANY_PARAMS,   \
+                                         0);                                \
+    silc_server_command_free(cmd);                                          \
+    return;                                                                 \
+  }                                                                         \
 } while(0)
 
 /* Returns TRUE if the connection is registered. Unregistered connections
@@ -130,8 +138,7 @@ static int silc_server_is_registered(SilcServer server,
     return TRUE;
 
   silc_server_command_send_status_reply(cmd, command,
-                                       SILC_STATUS_ERR_NOT_REGISTERED);
-  silc_server_command_free(cmd);
+                                       SILC_STATUS_ERR_NOT_REGISTERED, 0);
   return FALSE;
 }
 
@@ -149,6 +156,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 +171,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);
 }
@@ -181,7 +195,8 @@ void silc_server_command_process(SilcServer server,
   ctx->packet = silc_packet_context_dup(packet); /* Save original packet */
   
   /* Parse the command payload in the packet */
-  ctx->payload = silc_command_payload_parse(packet->buffer);
+  ctx->payload = silc_command_payload_parse(packet->buffer->data,
+                                           packet->buffer->len);
   if (!ctx->payload) {
     SILC_LOG_ERROR(("Bad command payload, packet dropped"));
     silc_buffer_free(packet->buffer);
@@ -198,9 +213,9 @@ 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_STATUS_ERR_UNKNOWN_COMMAND, 0);
     silc_server_command_free(ctx);
     return;
   }
@@ -221,26 +236,25 @@ void silc_server_command_process(SilcServer server,
       client->fast_command++;
       fast = FALSE;
     } else {
-      client->fast_command = ((client->fast_command - 1) <= 0 ? 0 : 
-                             client->fast_command--);
+      if (client->fast_command - 2 <= 0)
+       client->fast_command = 0;
+      else
+       client->fast_command -= 2;
       fast = TRUE;
     }
 
     if (!fast && ((cmd->flags & SILC_CF_LAG_STRICT) ||
                  (client->fast_command > 5 && cmd->flags & SILC_CF_LAG)))
       silc_schedule_task_add(server->schedule, sock->sock, 
-                        silc_server_command_process_timeout,
-                        (void *)timeout, 
-                        2 - (time(NULL) - client->last_command), 0,
-                        SILC_TASK_TIMEOUT,
-                        SILC_TASK_PRI_NORMAL);
+                            silc_server_command_process_timeout, timeout,
+                            (client->fast_command < 3 ? 0 :
+                             2 - (time(NULL) - client->last_command)),
+                            (client->fast_command < 3 ? 200000 : 0),
+                            SILC_TASK_TIMEOUT, SILC_TASK_PRI_NORMAL);
     else
       silc_schedule_task_add(server->schedule, sock->sock, 
-                        silc_server_command_process_timeout,
-                        (void *)timeout, 
-                        0, 1,
-                        SILC_TASK_TIMEOUT,
-                        SILC_TASK_PRI_NORMAL);
+                            silc_server_command_process_timeout, timeout,
+                            0, 1, SILC_TASK_TIMEOUT, SILC_TASK_PRI_NORMAL);
     return;
   }
 
@@ -250,6 +264,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 */
@@ -296,31 +312,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;
 
@@ -340,8 +368,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;
@@ -354,7 +382,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++;
     }
@@ -364,27 +391,20 @@ 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 
 silc_server_command_send_status_reply(SilcServerCommandContext cmd,
                                      SilcCommand command,
-                                     SilcCommandStatus status)
+                                     SilcStatus status,
+                                     SilcStatus error)
 {
   SilcBuffer buffer;
 
   SILC_LOG_DEBUG(("Sending command status %d", status));
 
   buffer = 
-    silc_command_reply_payload_encode_va(command, status, 
+    silc_command_reply_payload_encode_va(command, status, error,
                                         silc_command_get_ident(cmd->payload),
                                         0);
   silc_server_packet_send(cmd->server, cmd->sock,
@@ -399,17 +419,18 @@ silc_server_command_send_status_reply(SilcServerCommandContext cmd,
 static void 
 silc_server_command_send_status_data(SilcServerCommandContext cmd,
                                     SilcCommand command,
-                                    SilcCommandStatus status,
-                                    uint32 arg_type,
-                                    unsigned char *arg,
-                                    uint32 arg_len)
+                                    SilcStatus status,
+                                    SilcStatus error,
+                                    SilcUInt32 arg_type,
+                                    const unsigned char *arg,
+                                    SilcUInt32 arg_len)
 {
   SilcBuffer buffer;
 
   SILC_LOG_DEBUG(("Sending command status %d", status));
 
   buffer = 
-    silc_command_reply_payload_encode_va(command, status, 
+    silc_command_reply_payload_encode_va(command, status, 0,
                                         silc_command_get_ident(cmd->payload),
                                         1, arg_type, arg, arg_len);
   silc_server_packet_send(cmd->server, cmd->sock,
@@ -428,18 +449,20 @@ silc_server_command_pending_error_check(SilcServerCommandContext cmd,
                                        SilcServerCommandReplyContext cmdr,
                                        SilcCommand command)
 {
-  SilcCommandStatus status;
-
   if (!cmd->pending || !cmdr)
     return FALSE;
 
-  SILC_GET16_MSB(status, silc_argument_get_arg_type(cmdr->args, 1, NULL));
-  if (status != SILC_STATUS_OK &&
-      status != SILC_STATUS_LIST_START &&
-      status != SILC_STATUS_LIST_ITEM &&
-      status != SILC_STATUS_LIST_END) {
-    /* Send the error message */
-    silc_server_command_send_status_reply(cmd, command, status);
+  if (!silc_command_get_status(cmdr->payload, NULL, NULL)) {
+    SilcBuffer buffer;
+
+    /* Send the same command reply payload */
+    silc_command_set_ident(cmdr->payload, 
+                          silc_command_get_ident(cmd->payload));
+    buffer = silc_command_payload_encode_payload(cmdr->payload);
+    silc_server_packet_send(cmd->server, cmd->sock,
+                           SILC_PACKET_COMMAND_REPLY, 0, 
+                           buffer->data, buffer->len, FALSE);
+    silc_buffer_free(buffer);
     return TRUE;
   }
 
@@ -452,62 +475,70 @@ silc_server_command_pending_error_check(SilcServerCommandContext cmd,
 
 ******************************************************************************/
 
+typedef struct {
+  void *id;
+  SilcIdType id_type;
+  SilcUInt32 index;
+  SilcStatus error;
+} *ResolveError;
+
+#define ADD_ERROR(errptr, errptr_count, _id, _id_type, _index, _status)        \
+do {                                                                   \
+  errptr = silc_realloc(errptr, sizeof(*errptr) * (errptr_count + 1)); \
+  if (!errptr)                                                         \
+    return FALSE;                                                      \
+  errptr[errptr_count].id = _id;                                       \
+  errptr[errptr_count].id_type = _id_type;                             \
+  errptr[errptr_count].index = _index;                                 \
+  errptr[errptr_count].error = _status;                                        \
+  errptr_count++;                                                      \
+} while(0)
+
 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)
+                               ResolveError *error_client,
+                               SilcUInt32 *error_client_count)
 {
   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);
+  void *id;
   int i, k;
 
   /* If client ID is in the command it must be used instead of nickname */
-  tmp = silc_argument_get_arg_type(cmd->args, 3, &len);
+  tmp = silc_argument_get_arg_type(cmd->args, 4, &len);
   if (!tmp) {
     /* No ID, get the nickname@server string and parse it. */
     tmp = silc_argument_get_arg_type(cmd->args, 1, NULL);
     if (tmp) {
       silc_parse_userfqdn(tmp, nickname, server_name);
     } else {
-      silc_server_command_send_status_reply(cmd, command,
-                                           SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
+      silc_server_command_send_status_reply(cmd, SILC_COMMAND_WHOIS,
+                                           SILC_STATUS_ERR_BAD_NICKNAME, 0);
       return FALSE;
     }
   } else {
-    /* Command includes ID, we must use that.  Also check whether the command
-       has more than one ID set - take them all. */
-
-    *client_id = silc_calloc(1, sizeof(**client_id));
-    (*client_id)[0] = silc_id_payload_parse_id(tmp, len);
-    if ((*client_id)[0] == NULL) {
-      silc_free(*client_id);
-      return FALSE;
-    }
-    *client_id_count = 1;
-
-    /* Take all ID's from the command packet */
-    if (argc > 1) {
-      for (k = 1, i = 1; i < argc; i++) {
-       tmp = silc_argument_get_arg_type(cmd->args, i + 3, &len);
-       if (tmp) {
-         *client_id = silc_realloc(*client_id, sizeof(**client_id) *
-                                   (*client_id_count + 1));
-         (*client_id)[k] = silc_id_payload_parse_id(tmp, len);
-         if ((*client_id)[k] == NULL) {
-           /* Cleanup all and fail */
-           for (i = 0; i < *client_id_count; i++)
-             silc_free((*client_id)[i]);
-           silc_free(*client_id);
-           return FALSE;
-         }
-         (*client_id_count)++;
-         k++;
-       }
+    /* Command includes ID, we must use that.  Take all ID's from the 
+       command packet */
+    for (k = 0, i = 0; i < argc; i++) {
+      tmp = silc_argument_get_arg_type(cmd->args, i + 4, &len);
+      if (!tmp)
+       continue;
+      id = silc_id_payload_parse_id(tmp, len, NULL);
+      if (id) {
+       *client_id = silc_realloc(*client_id, sizeof(**client_id) *
+                                 (*client_id_count + 1));
+       (*client_id)[k] = id;
+       (*client_id_count)++;
+       k++;
+      } else {
+       ADD_ERROR((*error_client), (*error_client_count), NULL, 0, i + 4,
+                 SILC_STATUS_ERR_BAD_CLIENT_ID);
       }
     }
   }
@@ -515,7 +546,7 @@ silc_server_command_whois_parse(SilcServerCommandContext cmd,
   /* Get the max count of reply messages allowed */
   tmp = silc_argument_get_arg_type(cmd->args, 2, NULL);
   if (tmp)
-    *count = atoi(tmp);
+    SILC_GET32_MSB(*count, tmp);
   else
     *count = 0;
 
@@ -525,33 +556,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) {
@@ -559,7 +603,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;
@@ -611,7 +654,7 @@ silc_server_command_whois_check(SilcServerCommandContext cmd,
                                               sizeof(**r->res_argv));
        memcpy(r->res_argv[r->res_argc], idp->data, idp->len);
        r->res_argv_lens[r->res_argc] = idp->len;
-       r->res_argv_types[r->res_argc] = r->res_argc + 3;
+       r->res_argv_types[r->res_argc] = r->res_argc + 4;
        r->res_argc++;
        silc_buffer_free(idp);
 
@@ -642,7 +685,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;
@@ -663,85 +705,61 @@ 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, 
+                                    ResolveError errors,
+                                    SilcUInt32 errors_count,
+                                    int count, const char *nickname,
+                                    SilcClientID **client_ids)
 {
   SilcServer server = cmd->server;
   char *tmp;
-  int i, k, len;
-  SilcBuffer packet, idp, channels;
+  int i, k, valid_count = clients_count;
+  SilcUInt32 len;
+  SilcBuffer packet, idp, channels, umode_list = NULL;
   SilcClientEntry entry;
-  SilcCommandStatus status;
-  uint16 ident = silc_command_get_ident(cmd->payload);
+  SilcStatus status;
+  SilcUInt16 ident = silc_command_get_ident(cmd->payload);
   char nh[256], uh[256];
   unsigned char idle[4], mode[4];
+  unsigned char *fingerprint;
   SilcSocketConnection hsock;
 
-  len = 0;
-  for (i = 0; i < clients_count; i++)
-    if (clients[i]->data.status & SILC_IDLIST_STATUS_REGISTERED)
-      len++;
+  if (nickname) {
+    /* Process only valid clients and ignore those that are not registered. 
+       This is checked with nickname only because when resolved client IDs
+       we check that they are registered earlier. */
+    valid_count = 0;
+    for (i = 0; i < clients_count; i++)
+      if (clients[i]->data.status & SILC_IDLIST_STATUS_REGISTERED)
+       valid_count++;
+      else
+       clients[i] = NULL;
 
-  if (len == 0 && clients_count) {
-    entry = clients[0];
-    if (entry->nickname) {
-      silc_server_command_send_status_data(cmd, SILC_COMMAND_WHOIS,
-                                          SILC_STATUS_ERR_NO_SUCH_NICK,
-                                          3, entry->nickname, 
-                                          strlen(entry->nickname));
-    } else {
-      SilcBuffer idp = silc_id_payload_encode(entry->id, SILC_ID_CLIENT);
+    if (!valid_count) {
       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);
+                                          SILC_STATUS_ERR_NO_SUCH_NICK, 0,
+                                          3, nickname, strlen(nickname));
+      return;
     }
-
-    return;
   }
 
+  /* Start processing found clients. */
   status = SILC_STATUS_OK;
-  if (len > 1)
+  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) {
-       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 && !errors_count)
       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);
@@ -764,44 +782,47 @@ silc_server_command_whois_send_reply(SilcServerCommandContext cmd,
     }
       
     strncat(uh, entry->username, strlen(entry->username));
-    if (!strchr(entry->username, '@')) {
+    if (!strchr(entry->username, '@') && entry->connection) {
       strncat(uh, "@", 1);
       hsock = (SilcSocketConnection)entry->connection;
       len = strlen(hsock->hostname);
       strncat(uh, hsock->hostname, len);
     }
 
-    channels = silc_server_get_client_channel_list(server, entry);
+    if (cmd->sock->type == SILC_SOCKET_TYPE_CLIENT)
+      channels = silc_server_get_client_channel_list(server, entry, FALSE, 
+                                                    FALSE, &umode_list);
+    else
+      channels = silc_server_get_client_channel_list(server, entry, TRUE, 
+                                                    TRUE, &umode_list);
+
+    if (entry->data.fingerprint[0] != 0 && entry->data.fingerprint[1] != 0)
+      fingerprint = entry->data.fingerprint;
+    else
+      fingerprint = NULL;
       
     SILC_PUT32_MSB(entry->mode, mode);
-
-    if (entry->connection) {
+    if (entry->connection)
       SILC_PUT32_MSB((time(NULL) - entry->data.last_receive), idle);
-    }
 
-    if (channels)
-      packet = silc_command_reply_payload_encode_va(SILC_COMMAND_WHOIS,
-                                                   status, ident, 7, 
-                                                   2, idp->data, idp->len,
-                                                   3, nh, strlen(nh),
-                                                   4, uh, strlen(uh),
-                                                   5, entry->userinfo, 
-                                                   strlen(entry->userinfo),
-                                                   6, channels->data,
-                                                   channels->len,
-                                                   7, mode, 4,
-                                                   8, idle, 4);
-    else
-      packet = silc_command_reply_payload_encode_va(SILC_COMMAND_WHOIS,
-                                                   status, ident, 6, 
-                                                   2, idp->data, idp->len,
-                                                   3, nh, strlen(nh),
-                                                   4, uh, strlen(uh),
-                                                   5, entry->userinfo, 
-                                                   strlen(entry->userinfo),
-                                                   7, mode, 4,
-                                                   8, idle, 4);
-    
+    packet = 
+      silc_command_reply_payload_encode_va(SILC_COMMAND_WHOIS,
+                                          status, 0, ident, 9, 
+                                          2, idp->data, idp->len,
+                                          3, nh, strlen(nh),
+                                          4, uh, strlen(uh),
+                                          5, entry->userinfo, 
+                                          strlen(entry->userinfo),
+                                          6, channels ? channels->data : NULL,
+                                          channels ? channels->len : 0,
+                                          7, mode, 4,
+                                          8, idle, 4,
+                                          9, fingerprint,
+                                          fingerprint ? 20 : 0,
+                                          10, umode_list ? umode_list->data :
+                                          NULL, umode_list ? umode_list->len :
+                                          0);
+
     silc_server_packet_send(server, cmd->sock, SILC_PACKET_COMMAND_REPLY,
                            0, packet->data, packet->len, FALSE);
     
@@ -809,11 +830,80 @@ silc_server_command_whois_send_reply(SilcServerCommandContext cmd,
     silc_buffer_free(idp);
     if (channels)
       silc_buffer_free(channels);
+    if (umode_list) {
+      silc_buffer_free(umode_list);
+      umode_list = NULL;
+    }
+
+    if (status == SILC_STATUS_LIST_END)
+      break;
+    k++;
+  }
+
+  /* Send error replies */
+  if (status == SILC_STATUS_OK && errors_count > 1)
+    status = SILC_STATUS_LIST_START;
 
+  idp = NULL;
+  for (i = 0, k = 0; i < errors_count; i++) {
+    if (errors[i].id) {
+      idp = silc_id_payload_encode(errors[i].id, SILC_ID_CLIENT);
+      tmp = idp->data;
+      len = idp->len;
+    } else {
+      tmp = silc_argument_get_arg_type(cmd->args, errors[i].index, &len);
+    }
+      
+    if (k >= 1)
+      status = SILC_STATUS_LIST_ITEM;
+    if (errors_count > 1 && k == errors_count - 1)
+      status = SILC_STATUS_LIST_END;
+    if (count && k - 1 == count)
+      status = SILC_STATUS_LIST_END;
+      
+    /* Send error */
+    silc_server_command_send_status_data(cmd, SILC_COMMAND_WHOIS,
+                                        (status == SILC_STATUS_OK ?
+                                         errors[i].error : status),
+                                        (status == SILC_STATUS_OK ?
+                                         0 : errors[i].error),
+                                        2, tmp, len);
+    silc_buffer_free(idp);
+    idp = NULL;
+      
+    if (status == SILC_STATUS_LIST_END)
+      break;
     k++;
   }
 }
 
+static void 
+silc_server_command_whois_send_router(SilcServerCommandContext cmd)
+{
+  SilcServer server = cmd->server;
+  SilcBuffer tmpbuf;
+  SilcUInt16 old_ident;
+
+  old_ident = silc_command_get_ident(cmd->payload);
+  silc_command_set_ident(cmd->payload, ++server->cmd_ident);
+  tmpbuf = silc_command_payload_encode_payload(cmd->payload);
+
+  /* Send WHOIS command to our router */
+  silc_server_packet_send(server, (SilcSocketConnection)
+                         server->router->connection,
+                         SILC_PACKET_COMMAND, cmd->packet->flags,
+                         tmpbuf->data, tmpbuf->len, TRUE);
+
+  /* Reprocess this packet after received reply from router */
+  silc_server_command_pending(server, SILC_COMMAND_WHOIS, 
+                             silc_command_get_ident(cmd->payload),
+                             silc_server_command_whois,
+                             silc_server_command_dup(cmd));
+  cmd->pending = TRUE;
+  silc_command_set_ident(cmd->payload, old_ident);
+  silc_buffer_free(tmpbuf);
+}
+
 static int
 silc_server_command_whois_process(SilcServerCommandContext cmd)
 {
@@ -822,59 +912,34 @@ 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, error_client_count = 0;
+  ResolveError error_client = NULL;
   int i, ret = 0;
   bool check_global = FALSE;
 
-  /* Protocol dictates that we must always send the received WHOIS request
-     to our router if we are normal server, so let's do it now unless we
-     are standalone. We will not send any replies to the client until we
-     have received reply from the router. */
-  if (cmd->sock->type == SILC_SOCKET_TYPE_CLIENT &&
-      server->server_type == SILC_SERVER && !cmd->pending && 
-      !server->standalone) {
-    SilcBuffer tmpbuf;
-    uint16 old_ident;
-
-    old_ident = silc_command_get_ident(cmd->payload);
-    silc_command_set_ident(cmd->payload, silc_rng_get_rn16(server->rng));
-    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);
+  /* Parse the whois request */
+  if (!silc_server_command_whois_parse(cmd, &client_id, &client_id_count, 
+                                      &nick, &server_name, &count,
+                                      &error_client, &error_client_count))
+    return 0;
 
-    silc_buffer_free(tmpbuf);
+  /* Send the WHOIS request to the router only if it included nickname.
+     Since nicknames can be expanded into many clients we need to send it
+     to router.  If the WHOIS included only client ID's we will check them
+     first locally since we just might have them. */
+  if (nick && !client_id_count && cmd->sock->type == SILC_SOCKET_TYPE_CLIENT &&
+      server->server_type == SILC_SERVER && !cmd->pending &&
+      !server->standalone) {
+    silc_server_command_whois_send_router(cmd);
     ret = -1;
     goto out;
   }
 
-  /* We are ready to process the command request. Let's search for the
-     requested client and send reply to the requesting client. */
-
   if (cmd->sock->type == SILC_SOCKET_TYPE_CLIENT)
     check_global = TRUE;
   else if (server->server_type != SILC_SERVER)
     check_global = TRUE;
 
-  /* Parse the whois request */
-  if (!silc_server_command_whois_parse(cmd, &client_id, &client_id_count, 
-                                      &nick, &server_name, &count,
-                                      SILC_COMMAND_WHOIS))
-    return 0;
-
   /* Get all clients matching that ID or nickname from local list */
   if (client_id_count) {
     /* Check all Client ID's received in the command packet */
@@ -888,9 +953,23 @@ 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;
+       }
+
+       ADD_ERROR(error_client, error_client_count, client_id[i], 
+                 SILC_ID_CLIENT, 0, SILC_STATUS_ERR_NO_SUCH_CLIENT_ID);
       }
     }
-  } else {
+  } else if (nick) {
+    /* Find by nickname */
     if (!silc_idlist_get_clients_by_hash(server->local_list, 
                                         nick, server->md5hash,
                                         &clients, &clients_count))
@@ -907,19 +986,26 @@ silc_server_command_whois_process(SilcServerCommandContext cmd)
     }
   }
   
-  if (!clients) {
+  if (!clients && (client_id_count || nick)) {
+    /* 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) {
+    if (!client_id_count)
       silc_server_command_send_status_data(cmd, SILC_COMMAND_WHOIS,
-                                          SILC_STATUS_ERR_NO_SUCH_NICK,
+                                          SILC_STATUS_ERR_NO_SUCH_NICK, 0,
                                           3, nick, strlen(nick));
-    } else {
-      SilcBuffer idp = silc_id_payload_encode(client_id[0], SILC_ID_CLIENT);
-      silc_server_command_send_status_data(cmd, SILC_COMMAND_WHOIS,
-                                          SILC_STATUS_ERR_NO_SUCH_CLIENT_ID,
-                                          2, idp->data, idp->len);
-      silc_buffer_free(idp);
-    }
+    else
+      silc_server_command_whois_send_reply(cmd, NULL, 0,
+                                          error_client, error_client_count,
+                                          0, NULL, NULL);
     goto out;
   }
 
@@ -935,7 +1021,8 @@ silc_server_command_whois_process(SilcServerCommandContext cmd)
 
   /* Send the command reply */
   silc_server_command_whois_send_reply(cmd, clients, clients_count,
-                                      count);
+                                      error_client, error_client_count,
+                                      count, nick, client_id);
 
  out:
   if (client_id_count) {
@@ -944,6 +1031,7 @@ silc_server_command_whois_process(SilcServerCommandContext cmd)
     silc_free(client_id);
   }
   silc_free(clients);
+  silc_free(error_client);
   silc_free(nick);
   silc_free(server_name);
 
@@ -961,9 +1049,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);
 }
 
 /******************************************************************************
@@ -979,12 +1065,13 @@ 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) {
     silc_server_command_send_status_reply(cmd, SILC_COMMAND_WHOWAS,
-                                         SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
+                                         SILC_STATUS_ERR_NOT_ENOUGH_PARAMS, 
+                                         0);
     return FALSE;
   }
 
@@ -994,7 +1081,7 @@ silc_server_command_whowas_parse(SilcServerCommandContext cmd,
   /* Get the max count of reply messages allowed */
   tmp = silc_argument_get_arg_type(cmd->args, 2, NULL);
   if (tmp)
-    *count = atoi(tmp);
+    SILC_GET32_MSB(*count, tmp);
   else
     *count = 0;
 
@@ -1004,7 +1091,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;
@@ -1015,13 +1102,13 @@ silc_server_command_whowas_check(SilcServerCommandContext cmd,
 
     if (!entry->nickname || !entry->username) {
       SilcBuffer tmpbuf;
-      uint16 old_ident;
+      SilcUInt16 old_ident;
 
       if (!entry->router)
        continue;
       
       old_ident = silc_command_get_ident(cmd->payload);
-      silc_command_set_ident(cmd->payload, silc_rng_get_rn16(server->rng));
+      silc_command_set_ident(cmd->payload, ++server->cmd_ident);
       tmpbuf = silc_command_payload_encode_payload(cmd->payload);
 
       /* Send WHOWAS command */
@@ -1032,11 +1119,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);
@@ -1050,53 +1135,56 @@ 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;
+  SilcStatus status;
+  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 */
+    tmp = silc_argument_get_arg_type(cmd->args, 1, NULL);
+    silc_server_command_send_status_data(cmd, SILC_COMMAND_WHOWAS,
+                                        SILC_STATUS_ERR_NO_SUCH_NICK, 0,
+                                        3, tmp, tmp ? strlen(tmp) : 0);
+    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;
 
-    /* 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));
 
@@ -1119,35 +1207,25 @@ silc_server_command_whowas_send_reply(SilcServerCommandContext cmd,
       strcat(uh, "*private*");
     }
       
-    if (entry->userinfo)
-      packet = 
-       silc_command_reply_payload_encode_va(SILC_COMMAND_WHOWAS,
-                                            status, ident, 4, 
-                                            2, idp->data, idp->len,
-                                            3, nh, strlen(nh),
-                                            4, uh, strlen(uh),
-                                            5, entry->userinfo, 
-                                            strlen(entry->userinfo));
-    else
-      packet = 
-       silc_command_reply_payload_encode_va(SILC_COMMAND_WHOWAS,
-                                            status, ident, 3, 
-                                            2, idp->data, idp->len,
-                                            3, nh, strlen(nh),
-                                            4, uh, strlen(uh));
-
+    packet = 
+      silc_command_reply_payload_encode_va(SILC_COMMAND_WHOWAS,
+                                          status, 0, ident, 4, 
+                                          2, idp->data, idp->len,
+                                          3, nh, strlen(nh),
+                                          4, uh, strlen(uh),
+                                          5, entry->userinfo, 
+                                          entry->userinfo ? 
+                                          strlen(entry->userinfo) : 0);
     silc_server_packet_send(server, cmd->sock, SILC_PACKET_COMMAND_REPLY,
                            0, packet->data, packet->len, FALSE);
     
     silc_buffer_free(packet);
     silc_buffer_free(idp);
-  }
 
-  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));
+    if (status == SILC_STATUS_LIST_END)
+      break;
+    k++;
+  }
 }
 
 static int
@@ -1157,7 +1235,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;
 
@@ -1169,10 +1247,10 @@ 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, silc_rng_get_rn16(server->rng));
+    silc_command_set_ident(cmd->payload, ++server->cmd_ident);
     tmpbuf = silc_command_payload_encode_payload(cmd->payload);
 
     /* Send WHOWAS command to our router */
@@ -1184,11 +1262,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);
@@ -1229,7 +1305,7 @@ silc_server_command_whowas_process(SilcServerCommandContext cmd)
   if (!clients) {
     /* Such a client really does not exist in the SILC network. */
     silc_server_command_send_status_data(cmd, SILC_COMMAND_WHOWAS,
-                                        SILC_STATUS_ERR_NO_SUCH_NICK,
+                                        SILC_STATUS_ERR_NO_SUCH_NICK, 0,
                                         3, nick, strlen(nick));
     goto out;
   }
@@ -1246,7 +1322,6 @@ silc_server_command_whowas_process(SilcServerCommandContext cmd)
   silc_free(clients);
   silc_free(nick);
   silc_free(server_name);
-
   return ret;
 }
 
@@ -1260,9 +1335,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);
 }
 
 /******************************************************************************
@@ -1271,25 +1344,53 @@ 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)
+                                  SilcUInt32 *channels_count,
+                                  SilcUInt32 *count,
+                                  ResolveError *error_id,
+                                  SilcUInt32 *error_id_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;
   int i;
-  bool error = FALSE;
 
   if (cmd->sock->type == SILC_SOCKET_TYPE_CLIENT)
     check_global = TRUE;
@@ -1301,6 +1402,15 @@ silc_server_command_identify_parse(SilcServerCommandContext cmd,
   if (!tmp) {
     /* No ID, get the names. */
 
+    /* If we are normal server and have not resolved information from
+       router yet, do so now. */
+    if (cmd->sock->type == SILC_SOCKET_TYPE_CLIENT && 
+       server->server_type == SILC_SERVER && !cmd->pending && 
+       !server->standalone) {
+      silc_server_command_identify_send_router(cmd);
+      return -1;
+    }
+
     /* Try to get nickname@server. */
     tmp = silc_argument_get_arg_type(cmd->args, 1, NULL);
     if (tmp) {
@@ -1328,10 +1438,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,
+                                            SILC_STATUS_ERR_NO_SUCH_NICK, 0,
                                             3, tmp, strlen(tmp));
-       return FALSE;
+       return 0;
       }
     }
 
@@ -1350,10 +1461,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;
+                                            0, 3, tmp, strlen(tmp));
+       return 0;
       }
     }
 
@@ -1372,17 +1484,19 @@ 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;
+                                            0, 3, tmp, strlen(tmp));
+       return 0;
       }
     }
 
     if (!(*clients) && !(*servers) && !(*channels)) {
       silc_server_command_send_status_reply(cmd, SILC_COMMAND_IDENTIFY,
-                                           SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
-      return FALSE;
+                                           SILC_STATUS_ERR_NOT_ENOUGH_PARAMS,
+                                           0);
+      return 0;
     }
   } else {
     /* Command includes ID, we must use that.  Also check whether the command
@@ -1396,72 +1510,98 @@ silc_server_command_identify_parse(SilcServerCommandContext cmd,
       if (!tmp)
        continue;
       
-      idp = silc_id_payload_parse_data(tmp, len);
-      if (!idp) {
-       silc_free(*clients);
-       silc_free(*servers);
-       silc_free(*channels);
-       silc_server_command_send_status_reply(cmd, SILC_COMMAND_IDENTIFY,
-                                     SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
-       return FALSE;
-      }
+      idp = silc_id_payload_parse(tmp, len);
+      if (!idp)
+       ADD_ERROR((*error_id), (*error_id_count), NULL, 0, i + 5,
+                 SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
 
       id = silc_id_payload_get_id(idp);
-      
       switch (silc_id_payload_get_type(idp)) {
        
       case SILC_ID_CLIENT:
-       entry = (void *)silc_idlist_find_client_by_id(server->local_list, 
-                                                     id, TRUE, NULL);
+       entry = silc_idlist_find_client_by_id(server->local_list, 
+                                             id, TRUE, NULL);
        if (!entry && check_global)
-         entry = (void *)silc_idlist_find_client_by_id(server->global_list, 
-                                                       id, TRUE, NULL);
+         entry = silc_idlist_find_client_by_id(server->global_list, 
+                                               id, TRUE, NULL);
        if (entry) {
          *clients = silc_realloc(*clients, sizeof(**clients) * 
                                  (*clients_count + 1));
          (*clients)[(*clients_count)++] = (SilcClientEntry)entry;
        } else {
-         silc_server_command_send_status_data(cmd, SILC_COMMAND_IDENTIFY,
-                                       SILC_STATUS_ERR_NO_SUCH_CLIENT_ID,
-                                              2, tmp, len);
-         error = TRUE;
+         /* 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);
+           silc_free(*error_id);
+           return -1;
+         }
+
+         ADD_ERROR((*error_id), (*error_id_count), NULL, 0, i + 5,
+                   SILC_STATUS_ERR_NO_SUCH_CLIENT_ID);
        }
 
        break;
        
       case SILC_ID_SERVER:
-       entry = (void *)silc_idlist_find_server_by_id(server->local_list, 
-                                                     id, TRUE, NULL);
+       entry = silc_idlist_find_server_by_id(server->local_list, 
+                                             id, TRUE, NULL);
        if (!entry && check_global)
-         entry = (void *)silc_idlist_find_server_by_id(server->global_list, 
-                                                       id, TRUE, NULL);
+         entry = silc_idlist_find_server_by_id(server->global_list, 
+                                               id, TRUE, NULL);
        if (entry) {
          *servers = silc_realloc(*servers, sizeof(**servers) * 
                                  (*servers_count + 1));
          (*servers)[(*servers_count)++] = (SilcServerEntry)entry;
        } else {
-         silc_server_command_send_status_data(cmd, SILC_COMMAND_IDENTIFY,
-                                       SILC_STATUS_ERR_NO_SUCH_SERVER_ID,
-                                              2, tmp, len);
-         error = TRUE;
+         /* 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);
+           silc_free(*error_id);
+           return -1;
+         }
+
+         ADD_ERROR((*error_id), (*error_id_count), NULL, 0, i + 5,
+                   SILC_STATUS_ERR_NO_SUCH_SERVER_ID);
        }
        break;
        
       case SILC_ID_CHANNEL:
-       entry = (void *)silc_idlist_find_channel_by_id(server->local_list, 
-                                                      id, NULL);
+       entry = silc_idlist_find_channel_by_id(server->local_list, id, NULL);
        if (!entry && check_global)
-         entry = (void *)silc_idlist_find_channel_by_id(server->global_list, 
-                                                        id, NULL);
+         entry = silc_idlist_find_channel_by_id(server->global_list, id,
+                                                NULL);
        if (entry) {
          *channels = silc_realloc(*channels, sizeof(**channels) * 
                                   (*channels_count + 1));
          (*channels)[(*channels_count)++] = (SilcChannelEntry)entry;
        } else {
-         silc_server_command_send_status_data(cmd, SILC_COMMAND_IDENTIFY,
-                                       SILC_STATUS_ERR_NO_SUCH_CHANNEL_ID,
-                                              2, tmp, len);
-         error = TRUE;
+         /* 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);
+           silc_free(*error_id);
+           return -1;
+         }
+
+         ADD_ERROR((*error_id), (*error_id_count), NULL, 0, i + 5,
+                   SILC_STATUS_ERR_NO_SUCH_CHANNEL_ID);
        }
        break;
       }
@@ -1470,21 +1610,14 @@ silc_server_command_identify_parse(SilcServerCommandContext cmd,
     }
   }
 
-  if (error) {
-    silc_free(*clients);
-    silc_free(*servers);
-    silc_free(*channels);
-    return FALSE;
-  }
-  
   /* Get the max count of reply messages allowed */
   tmp = silc_argument_get_arg_type(cmd->args, 4, NULL);
   if (tmp)
-    *count = atoi(tmp);
+    SILC_GET32_MSB(*count, tmp);
   else
     *count = 0;
 
-  return TRUE;
+  return 1;
 }
 
 /* Checks that all mandatory fields in client entry are present. If not
@@ -1494,23 +1627,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) {
@@ -1518,7 +1662,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;
@@ -1570,7 +1713,7 @@ silc_server_command_identify_check_client(SilcServerCommandContext cmd,
                                               sizeof(**r->res_argv));
        memcpy(r->res_argv[r->res_argc], idp->data, idp->len);
        r->res_argv_lens[r->res_argc] = idp->len;
-       r->res_argv_types[r->res_argc] = r->res_argc + 3;
+       r->res_argv_types[r->res_argc] = r->res_argc + 4;
        r->res_argc++;
        silc_buffer_free(idp);
 
@@ -1601,7 +1744,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;
@@ -1622,82 +1764,76 @@ 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,
+                                       ResolveError errors,
+                                       SilcUInt32 errors_count,
                                        int count)
 {
   SilcServer server = cmd->server;
-  int i, k, len;
+  int i, k, valid_count;
+  SilcUInt32 len;
   SilcBuffer packet, idp;
-  SilcCommandStatus status;
-  uint16 ident = silc_command_get_ident(cmd->payload);
+  SilcStatus status;
+  SilcUInt16 ident = silc_command_get_ident(cmd->payload);
   char nh[256], uh[256];
   SilcSocketConnection hsock;
+  unsigned char *tmp;
 
   status = SILC_STATUS_OK;
 
   if (clients) {
     SilcClientEntry entry;
+    valid_count = clients_count;
+
+    if (silc_argument_get_arg_type(cmd->args, 1, NULL)) {
+      /* Process only valid clients and ignore those that are not registered. 
+        This is checked with nickname only because when resolved client IDs
+        we check that they are registered earlier. */
+      valid_count = 0;
+      for (i = 0; i < clients_count; i++) {
+       if (clients[i]->data.status & SILC_IDLIST_STATUS_REGISTERED)
+         valid_count++;
+       else
+         clients[i] = NULL;
+      }
 
-    len = 0;
-    for (i = 0; i < clients_count; i++)
-      if (clients[i]->data.status & SILC_IDLIST_STATUS_REGISTERED)
-       len++;
-
-    if (len == 0 && clients_count) {
-      entry = clients[0];
-      if (entry->nickname) {
+      if (!valid_count) {
+       /* No valid entries found at all, just send error */
+       tmp = silc_argument_get_arg_type(cmd->args, 1, NULL);
        silc_server_command_send_status_data(cmd, SILC_COMMAND_IDENTIFY,
-                                            SILC_STATUS_ERR_NO_SUCH_NICK,
-                                            3, entry->nickname, 
-                                            strlen(entry->nickname));
-      } else {
-       SilcBuffer idp = silc_id_payload_encode(entry->id, SILC_ID_CLIENT);
-       silc_server_command_send_status_data(cmd, SILC_COMMAND_IDENTIFY,
-                                            SILC_STATUS_ERR_NO_SUCH_CLIENT_ID,
-                                            2, idp->data, idp->len);
-       silc_buffer_free(idp);
+                                            SILC_STATUS_ERR_NO_SUCH_NICK, 0,
+                                            3, tmp, tmp ? strlen(tmp) : 0);
+       return;
       }
-      
-      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 
-         && !servers_count && !channels_count)
+      if (valid_count > 1 && k == valid_count - 1 
+         && !servers_count && !channels_count && !errors_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);
@@ -1710,15 +1846,15 @@ 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,
+                                                     status, 0, ident, 2,
                                                      2, idp->data, idp->len, 
                                                      3, nh, strlen(nh));
       } else {
        strncat(uh, entry->username, strlen(entry->username));
-       if (!strchr(entry->username, '@')) {
+       if (!strchr(entry->username, '@') && entry->connection) {
          strncat(uh, "@", 1);
          hsock = (SilcSocketConnection)entry->connection;
          len = strlen(hsock->hostname);
@@ -1726,7 +1862,7 @@ silc_server_command_identify_send_reply(SilcServerCommandContext cmd,
        }
        
        packet = silc_command_reply_payload_encode_va(SILC_COMMAND_IDENTIFY,
-                                                     status, ident, 3,
+                                                     status, 0, ident, 3,
                                                      2, idp->data, idp->len, 
                                                      3, nh, strlen(nh),
                                                      4, uh, strlen(uh));
@@ -1738,13 +1874,12 @@ silc_server_command_identify_send_reply(SilcServerCommandContext cmd,
       silc_buffer_free(packet);
       silc_buffer_free(idp);
       
+      if (status == SILC_STATUS_LIST_END)
+       break;
       k++;
     }
   }
 
-  status = (status == SILC_STATUS_LIST_ITEM ? 
-           SILC_STATUS_LIST_ITEM : SILC_STATUS_OK);
-
   if (servers) {
     SilcServerEntry entry;
 
@@ -1756,41 +1891,33 @@ silc_server_command_identify_send_reply(SilcServerCommandContext cmd,
       
       if (k >= 1)
        status = SILC_STATUS_LIST_ITEM;
-      if (servers_count > 1 && k == servers_count - 1 && !channels_count)
+      if (servers_count > 1 && k == servers_count - 1 && !channels_count &&
+         !errors_count)
        status = SILC_STATUS_LIST_END;
       if (count && k - 1 == count)
        status = SILC_STATUS_LIST_END;
-      if (count && k - 1 > count)
-       break;
       
       /* Send IDENTIFY reply */
       idp = silc_id_payload_encode(entry->id, SILC_ID_SERVER);
-      if (entry->server_name) {
-       packet = 
-         silc_command_reply_payload_encode_va(SILC_COMMAND_IDENTIFY,
-                                              status, ident, 2,
-                                              2, idp->data, idp->len, 
-                                              3, entry->server_name, 
-                                              strlen(entry->server_name));
-      } else {
-       packet = silc_command_reply_payload_encode_va(SILC_COMMAND_IDENTIFY,
-                                                     status, ident, 1,
-                                                     2, idp->data, idp->len);
-      }
-      
+      packet = 
+       silc_command_reply_payload_encode_va(SILC_COMMAND_IDENTIFY,
+                                            status, 0, ident, 2,
+                                            2, idp->data, idp->len, 
+                                            3, entry->server_name, 
+                                            entry->server_name ? 
+                                            strlen(entry->server_name) : 0);
       silc_server_packet_send(server, cmd->sock, SILC_PACKET_COMMAND_REPLY,
                              0, packet->data, packet->len, FALSE);
       
       silc_buffer_free(packet);
       silc_buffer_free(idp);
       
+      if (status == SILC_STATUS_LIST_END)
+       break;
       k++;
     }
   }
 
-  status = (status == SILC_STATUS_LIST_ITEM ? 
-           SILC_STATUS_LIST_ITEM : SILC_STATUS_OK);
-
   if (channels) {
     SilcChannelEntry entry;
 
@@ -1802,34 +1929,66 @@ silc_server_command_identify_send_reply(SilcServerCommandContext cmd,
       
       if (k >= 1)
        status = SILC_STATUS_LIST_ITEM;
-      if (channels_count > 1 && k == channels_count - 1)
+      if (channels_count > 1 && k == channels_count - 1 && !errors_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_CHANNEL);
-      if (entry->channel_name) {
-       packet = 
-         silc_command_reply_payload_encode_va(SILC_COMMAND_IDENTIFY,
-                                              status, ident, 2,
-                                              2, idp->data, idp->len, 
-                                              3, entry->channel_name, 
-                                              strlen(entry->channel_name));
-      } else {
-       packet = silc_command_reply_payload_encode_va(SILC_COMMAND_IDENTIFY,
-                                                     status, ident, 1,
-                                                     2, idp->data, idp->len);
-      }
-      
+      packet = 
+       silc_command_reply_payload_encode_va(SILC_COMMAND_IDENTIFY,
+                                            status, 0, ident, 2,
+                                            2, idp->data, idp->len, 
+                                            3, entry->channel_name, 
+                                            entry->channel_name ? 
+                                            strlen(entry->channel_name): 0);
       silc_server_packet_send(server, cmd->sock, SILC_PACKET_COMMAND_REPLY,
                              0, packet->data, packet->len, FALSE);
       
       silc_buffer_free(packet);
       silc_buffer_free(idp);
       
+      if (status == SILC_STATUS_LIST_END)
+       break;
+      k++;
+    }
+  }
+
+  /* Send error replies */
+  if (errors) {
+    if (status == SILC_STATUS_OK && errors_count > 1)
+      status = SILC_STATUS_LIST_START;
+
+    idp = NULL;
+    for (i = 0, k = 0; i < errors_count; i++) {
+      if (errors[i].id) {
+       idp = silc_id_payload_encode(errors[i].id, SILC_ID_CLIENT);
+       tmp = idp->data;
+       len = idp->len;
+      } else {
+       tmp = silc_argument_get_arg_type(cmd->args, errors[i].index, &len);
+      }
+      
+      if (k >= 1)
+       status = SILC_STATUS_LIST_ITEM;
+      if (errors_count > 1 && k == errors_count - 1)
+       status = SILC_STATUS_LIST_END;
+      if (count && k - 1 == count)
+       status = SILC_STATUS_LIST_END;
+      
+      /* Send error */
+      silc_server_command_send_status_data(cmd, SILC_COMMAND_IDENTIFY,
+                                          (status == SILC_STATUS_OK ?
+                                           errors[i].error : status),
+                                          (status == SILC_STATUS_OK ?
+                                           0 : errors[i].error),
+                                          2, tmp, len);
+      silc_buffer_free(idp);
+      idp = NULL;
+      
+      if (status == SILC_STATUS_LIST_END)
+       break;
       k++;
     }
   }
@@ -1838,64 +1997,29 @@ 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;
-
-  /* Protocol dictates that we must always send the received IDENTIFY request
-     to our router if we are normal server, so let's do it now unless we
-     are standalone. We will not send any replies to the client until we
-     have received reply from the router. */
-  if (cmd->sock->type == SILC_SOCKET_TYPE_CLIENT && 
-      server->server_type == SILC_SERVER && !cmd->pending && 
-      !server->standalone) {
-    SilcBuffer tmpbuf;
-    uint16 old_ident;
-
-    old_ident = silc_command_get_ident(cmd->payload);
-    silc_command_set_ident(cmd->payload, silc_rng_get_rn16(server->rng));
-    tmpbuf = silc_command_payload_encode_payload(cmd->payload);
-
-    /* Send IDENTIFY command to our router */
-    silc_server_packet_send(server, (SilcSocketConnection)
-                           server->router->connection,
-                           SILC_PACKET_COMMAND, cmd->packet->flags,
-                           tmpbuf->data, tmpbuf->len, TRUE);
-
-    /* Reprocess this packet after received reply from router */
-    silc_server_command_pending(server, SILC_COMMAND_IDENTIFY, 
-                               silc_command_get_ident(cmd->payload),
-                               silc_server_command_destructor,
-                               silc_server_command_identify,
-                               silc_server_command_dup(cmd));
-    cmd->pending = TRUE;
-
-    silc_command_set_ident(cmd->payload, old_ident);
-
-    silc_buffer_free(tmpbuf);
-    ret = -1;
-    goto out;
-  }
-
-  /* We are ready to process the command request. Let's search for the
-     requested client and send reply to the requesting client. */
+  SilcUInt32 clients_count = 0, servers_count = 0, channels_count = 0;
+  SilcUInt32 errors_count = 0;
+  ResolveError errors = NULL;
 
   /* Parse the IDENTIFY request */
-  if (!silc_server_command_identify_parse(cmd,
-                                         &clients, &clients_count,
-                                         &servers, &servers_count,
-                                         &channels, &channels_count,
-                                         &count))
-    return 0;
+  ret = silc_server_command_identify_parse(cmd,
+                                          &clients, &clients_count,
+                                          &servers, &servers_count,
+                                          &channels, &channels_count,
+                                          &count, &errors, &errors_count);
+  if (ret < 1)
+    return ret;
+  ret = 0;
 
   /* Check that all mandatory fields are present and request those data
      from the server who owns the client if necessary. */
-  if (clients && !silc_server_command_identify_check_client(cmd, clients, 
-                                                           clients_count)) {
+  if (!silc_server_command_identify_check_client(cmd, clients, 
+                                                clients_count)) {
     ret = -1;
     goto out;
   }
@@ -1905,13 +2029,14 @@ silc_server_command_identify_process(SilcServerCommandContext cmd)
                                          clients, clients_count,
                                          servers, servers_count,
                                          channels, channels_count, 
+                                         errors, errors_count,
                                          count);
 
  out:
   silc_free(clients);
   silc_free(servers);
   silc_free(channels);
-
+  silc_free(errors);
   return ret;
 }
 
@@ -1923,31 +2048,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;
-    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
@@ -1959,10 +2060,11 @@ SILC_SERVER_CMD_FUNC(nick)
   SilcServerCommandContext cmd = (SilcServerCommandContext)context;
   SilcClientEntry client = (SilcClientEntry)cmd->sock->user_data;
   SilcServer server = cmd->server;
-  SilcBuffer packet, nidp, oidp;
+  SilcBuffer packet, nidp, oidp = NULL;
   SilcClientID *new_id;
+  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)
@@ -1971,15 +2073,20 @@ 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);
+                                         SILC_STATUS_ERR_BAD_NICKNAME, 0);
     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);
+    goto send_reply;
+  }
 
   /* Create new Client ID */
   while (!silc_id_create_client_id(cmd->server, cmd->server->id, 
@@ -1987,6 +2094,11 @@ SILC_SERVER_CMD_FUNC(nick)
                                   cmd->server->md5hash, nick,
                                   &new_id)) {
     nickfail++;
+    if (nickfail > 9) {
+      silc_server_command_send_status_reply(cmd, SILC_COMMAND_NICK,
+                                           SILC_STATUS_ERR_BAD_NICKNAME, 0);
+      goto out;
+    }
     snprintf(&nick[strlen(nick) - 1], 1, "%d", nickfail);
   }
 
@@ -1997,44 +2109,56 @@ 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);
+
+  /* Check if anyone is watching the old nickname */
+  if (server->server_type == SILC_ROUTER)
+    silc_server_check_watcher_list(server, client, nick,
+                                  SILC_NOTIFY_TYPE_NICK_CHANGE);
 
   oidp = silc_id_payload_encode(client->id, SILC_ID_CLIENT);
 
   /* Remove old cache entry */
   silc_idcache_del_by_context(server->local_list->clients, client);
 
-  /* Free old ID */
   silc_free(client->id);
+  client->id = new_id;
 
-  /* Save the nickname as this client is our local client */
   silc_free(client->nickname);
-
   client->nickname = strdup(nick);
-  client->id = new_id;
 
   /* Update client cache */
   silc_idcache_add(server->local_list->clients, client->nickname, 
-                  client->id, (void *)client, 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));
 
+  /* Check if anyone is watching the new nickname */
+  if (server->server_type == SILC_ROUTER)
+    silc_server_check_watcher_list(server, client, NULL,
+                                  SILC_NOTIFY_TYPE_NICK_CHANGE);
+
+ send_reply:
   /* Send the new Client ID as reply command back to client */
   packet = silc_command_reply_payload_encode_va(SILC_COMMAND_NICK, 
-                                               SILC_STATUS_OK, ident, 1, 
-                                               2, nidp->data, nidp->len);
+                                               SILC_STATUS_OK, 0, ident, 2,
+                                               2, nidp->data, nidp->len,
+                                               3, nick, strlen(nick));
   silc_server_packet_send(cmd->server, cmd->sock, SILC_PACKET_COMMAND_REPLY,
                          0, packet->data, packet->len, FALSE);
 
   silc_buffer_free(packet);
   silc_buffer_free(nidp);
-  silc_buffer_free(oidp);
+  if (oidp)
+    silc_buffer_free(oidp);
   
  out:
   silc_server_command_free(cmd);
@@ -2045,43 +2169,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);
+  SilcStatus status;
+  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 == lch_count - 1 && gch_count)
-      break;
-    if (lch_count > 1 && i == lch_count - 1)
+    if (valid_lcount > 1 && k == valid_lcount - 1 && !valid_rcount)
       status = SILC_STATUS_LIST_END;
 
     idp = silc_id_payload_encode(entry->id, SILC_ID_CHANNEL);
@@ -2096,43 +2223,31 @@ silc_server_command_list_send_reply(SilcServerCommandContext cmd,
     }
 
     /* Send the reply */
-    if (topic)
-      packet = 
-       silc_command_reply_payload_encode_va(SILC_COMMAND_LIST, 
-                                            status, ident, 4, 
-                                            2, idp->data, idp->len,
-                                            3, entry->channel_name, 
-                                            strlen(entry->channel_name),
-                                            4, topic, strlen(topic),
-                                            5, usercount, 4);
-    else
-      packet = 
-       silc_command_reply_payload_encode_va(SILC_COMMAND_LIST, 
-                                            status, ident, 3, 
-                                            2, idp->data, idp->len,
-                                            3, entry->channel_name, 
-                                            strlen(entry->channel_name),
-                                            5, usercount, 4);
+    packet = 
+      silc_command_reply_payload_encode_va(SILC_COMMAND_LIST, 
+                                          status, 0, ident, 4,
+                                          2, idp->data, idp->len,
+                                          3, entry->channel_name, 
+                                          strlen(entry->channel_name),
+                                          4, topic, topic ? strlen(topic) : 0,
+                                          5, usercount, 4);
     silc_server_packet_send(cmd->server, cmd->sock, 
                            SILC_PACKET_COMMAND_REPLY, 0, packet->data, 
                            packet->len, FALSE);
     silc_buffer_free(packet);
     silc_buffer_free(idp);
+    k++;
   }
 
-  status = i ? SILC_STATUS_LIST_ITEM : SILC_STATUS_OK;
-
   /* Global list */
-  for (i = 0; i < gch_count; i++) {
+  for (i = 0, k = 0; i < gch_count; i++) {
     entry = gch[i];
-
     if (!entry)
       continue;
 
-    if (i >= 1)
+    if (k >= 1)
       status = SILC_STATUS_LIST_ITEM;
-
-    if (gch_count > 1 && i == lch_count - 1)
+    if (valid_rcount > 1 && k == valid_rcount - 1)
       status = SILC_STATUS_LIST_END;
 
     idp = silc_id_payload_encode(entry->id, SILC_ID_CHANNEL);
@@ -2142,33 +2257,25 @@ 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);
     }
 
     /* Send the reply */
-    if (topic)
-      packet = 
-       silc_command_reply_payload_encode_va(SILC_COMMAND_LIST, 
-                                            status, ident, 4, 
-                                            2, idp->data, idp->len,
-                                            3, entry->channel_name, 
-                                            strlen(entry->channel_name),
-                                            4, topic, strlen(topic),
-                                            5, usercount, 4);
-    else
-      packet = 
-       silc_command_reply_payload_encode_va(SILC_COMMAND_LIST, 
-                                            status, ident, 3, 
-                                            2, idp->data, idp->len,
-                                            3, entry->channel_name, 
-                                            strlen(entry->channel_name),
-                                            5, usercount, 4);
+    packet = 
+      silc_command_reply_payload_encode_va(SILC_COMMAND_LIST, 
+                                          status, 0, ident, 4,
+                                          2, idp->data, idp->len,
+                                          3, entry->channel_name, 
+                                          strlen(entry->channel_name),
+                                          4, topic, topic ? strlen(topic) : 0,
+                                          5, usercount, 4);
     silc_server_packet_send(cmd->server, cmd->sock, 
                            SILC_PACKET_COMMAND_REPLY, 0, packet->data, 
                            packet->len, FALSE);
     silc_buffer_free(packet);
     silc_buffer_free(idp);
+    k++;
   }
 }
 
@@ -2181,19 +2288,44 @@ 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, 2);
+  SILC_SERVER_COMMAND_CHECK(SILC_COMMAND_LIST, cmd, 0, 1);
+
+  /* If we are normal server, send the command to router, since we
+     want to know all channels in the network. */
+  if (!cmd->pending && server->server_type == SILC_SERVER && 
+      !server->standalone) {
+    SilcBuffer tmpbuf;
+    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);
+    silc_server_packet_send(server, server->router->connection,
+                           SILC_PACKET_COMMAND, cmd->packet->flags,
+                           tmpbuf->data, tmpbuf->len, TRUE);
+
+    /* Reprocess this packet after received reply from router */
+    silc_server_command_pending(server, SILC_COMMAND_LIST, 
+                               silc_command_get_ident(cmd->payload),
+                               silc_server_command_list, 
+                               silc_server_command_dup(cmd));
+    cmd->pending = TRUE;
+    silc_command_set_ident(cmd->payload, old_ident);
+    silc_buffer_free(tmpbuf);
+    goto out;
+  }
 
   /* Get Channel ID */
   tmp = silc_argument_get_arg_type(cmd->args, 1, &tmp_len);
   if (tmp) {
-    channel_id = silc_id_payload_parse_id(tmp, tmp_len);
+    channel_id = silc_id_payload_parse_id(tmp, tmp_len, NULL);
     if (!channel_id) {
       silc_server_command_send_status_reply(cmd, SILC_COMMAND_LIST,
-                                           SILC_STATUS_ERR_NO_CHANNEL_ID);
+                                           SILC_STATUS_ERR_NO_CHANNEL_ID, 0);
       goto out;
     }
   }
@@ -2202,15 +2334,17 @@ SILC_SERVER_CMD_FUNC(list)
   lchannels = silc_idlist_get_channels(server->local_list, channel_id,
                                       &lch_count);
   
-  /* Get the channels from global list if we are router */
-  if (server->server_type != SILC_SERVER) 
-    gchannels = silc_idlist_get_channels(server->global_list, channel_id,
-                                        &gch_count);
+  /* Get the channels from global list */
+  gchannels = silc_idlist_get_channels(server->global_list, channel_id,
+                                      &gch_count);
 
   /* Send the reply */
   silc_server_command_list_send_reply(cmd, lchannels, lch_count, 
                                      gchannels, gch_count);
 
+  silc_free(lchannels);
+  silc_free(gchannels);
+
  out:
   silc_server_command_free(cmd);
 }
@@ -2228,8 +2362,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);
 
@@ -2239,13 +2373,13 @@ SILC_SERVER_CMD_FUNC(topic)
   tmp = silc_argument_get_arg_type(cmd->args, 1, &tmp_len);
   if (!tmp) {
     silc_server_command_send_status_reply(cmd, SILC_COMMAND_TOPIC,
-                                         SILC_STATUS_ERR_NO_CHANNEL_ID);
+                                         SILC_STATUS_ERR_NO_CHANNEL_ID, 0);
     goto out;
   }
-  channel_id = silc_id_payload_parse_id(tmp, tmp_len);
+  channel_id = silc_id_payload_parse_id(tmp, tmp_len, NULL);
   if (!channel_id) {
     silc_server_command_send_status_reply(cmd, SILC_COMMAND_TOPIC,
-                                         SILC_STATUS_ERR_NO_CHANNEL_ID);
+                                         SILC_STATUS_ERR_NO_CHANNEL_ID, 0);
     goto out;
   }
 
@@ -2257,7 +2391,8 @@ SILC_SERVER_CMD_FUNC(topic)
                                             channel_id, NULL);
     if (!channel) {
       silc_server_command_send_status_reply(cmd, SILC_COMMAND_TOPIC,
-                                           SILC_STATUS_ERR_NO_SUCH_CHANNEL);
+                                           SILC_STATUS_ERR_NO_SUCH_CHANNEL,
+                                           0);
       goto out;
     }
   }
@@ -2267,30 +2402,33 @@ SILC_SERVER_CMD_FUNC(topic)
     tmp = silc_argument_get_arg_type(cmd->args, 2, NULL);
     if (!tmp) {
       silc_server_command_send_status_reply(cmd, SILC_COMMAND_TOPIC,
-                                           SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
+                                           SILC_STATUS_ERR_NOT_ENOUGH_PARAMS,
+                                           0);
       goto out;
     }
 
     if (strlen(tmp) > 256) {
       silc_server_command_send_status_reply(cmd, SILC_COMMAND_TOPIC,
-                                           SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
+                                           SILC_STATUS_ERR_NOT_ENOUGH_PARAMS,
+                                           0);
       goto out;
     }
 
     /* See whether the client is on channel and has rights to change topic */
-    if (!silc_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);
+                                           SILC_STATUS_ERR_NOT_ON_CHANNEL,
+                                           0);
       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 (channel->mode & SILC_CHANNEL_MODE_TOPIC &&
+       !(chl->mode & SILC_CHANNEL_UMODE_CHANOP) &&
+       !(chl->mode & SILC_CHANNEL_UMODE_CHANFO)) {
+      silc_server_command_send_status_reply(cmd, SILC_COMMAND_TOPIC,
+                                           SILC_STATUS_ERR_NO_CHANNEL_PRIV,
+                                           0);
+      goto out;
     }
 
     /* Set the topic for channel */
@@ -2301,13 +2439,14 @@ SILC_SERVER_CMD_FUNC(topic)
     if (!server->standalone)
       silc_server_send_notify_topic_set(server, server->router->connection,
                                        server->server_type == SILC_ROUTER ?
-                                       TRUE : FALSE, channel, client->id,
+                                       TRUE : FALSE, channel, 
+                                       client->id, SILC_ID_CLIENT,
                                        channel->topic);
 
     idp = silc_id_payload_encode(client->id, SILC_ID_CLIENT);
 
     /* Send notify about topic change to all clients on the channel */
-    silc_server_send_notify_to_channel(server, NULL, channel, TRUE,
+    silc_server_send_notify_to_channel(server, NULL, channel, FALSE, 
                                       SILC_NOTIFY_TYPE_TOPIC_SET, 2,
                                       idp->data, idp->len,
                                       channel->topic, strlen(channel->topic));
@@ -2316,16 +2455,12 @@ SILC_SERVER_CMD_FUNC(topic)
 
   /* Send the topic to client as reply packet */
   idp = silc_id_payload_encode(channel_id, SILC_ID_CHANNEL);
-  if (channel->topic)
-    packet = silc_command_reply_payload_encode_va(SILC_COMMAND_TOPIC, 
-                                                 SILC_STATUS_OK, ident, 2, 
-                                                 2, idp->data, idp->len,
-                                                 3, channel->topic, 
-                                                 strlen(channel->topic));
-  else
-    packet = silc_command_reply_payload_encode_va(SILC_COMMAND_TOPIC, 
-                                                 SILC_STATUS_OK, ident, 1, 
-                                                 2, idp->data, idp->len);
+  packet = silc_command_reply_payload_encode_va(SILC_COMMAND_TOPIC, 
+                                               SILC_STATUS_OK, 0, ident, 2, 
+                                               2, idp->data, idp->len,
+                                               3, channel->topic, 
+                                               channel->topic ? 
+                                               strlen(channel->topic) : 0);
   silc_server_packet_send(cmd->server, cmd->sock, SILC_PACKET_COMMAND_REPLY,
                          0, packet->data, packet->len, FALSE);
 
@@ -2353,8 +2488,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);
 
@@ -2362,13 +2497,13 @@ SILC_SERVER_CMD_FUNC(invite)
   tmp = silc_argument_get_arg_type(cmd->args, 1, &len);
   if (!tmp) {
     silc_server_command_send_status_reply(cmd, SILC_COMMAND_INVITE,
-                                         SILC_STATUS_ERR_NO_CHANNEL_ID);
+                                         SILC_STATUS_ERR_NO_CHANNEL_ID, 0);
     goto out;
   }
-  channel_id = silc_id_payload_parse_id(tmp, len);
+  channel_id = silc_id_payload_parse_id(tmp, len, NULL);
   if (!channel_id) {
     silc_server_command_send_status_reply(cmd, SILC_COMMAND_INVITE,
-                                         SILC_STATUS_ERR_NO_CHANNEL_ID);
+                                         SILC_STATUS_ERR_NO_CHANNEL_ID, 0);
     goto out;
   }
 
@@ -2380,48 +2515,51 @@ SILC_SERVER_CMD_FUNC(invite)
                                             channel_id, NULL);
     if (!channel) {
       silc_server_command_send_status_reply(cmd, SILC_COMMAND_INVITE,
-                                           SILC_STATUS_ERR_NO_SUCH_CHANNEL);
+                                           SILC_STATUS_ERR_NO_SUCH_CHANNEL,
+                                           0);
       goto out;
     }
   }
 
   /* Check whether the sender of this command is on the channel. */
   sender = (SilcClientEntry)sock->user_data;
-  if (!silc_server_client_on_channel(sender, channel)) {
+  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);
+                                         SILC_STATUS_ERR_NOT_ON_CHANNEL, 0);
     goto out;
   }
 
   /* Check whether the channel is invite-only channel. If yes then the
      sender of this command must be at least channel operator. */
-  if (channel->mode & SILC_CHANNEL_MODE_INVITE) {
-    silc_hash_table_find(channel->user_list, sender, NULL, (void *)&chl);
-    if (chl->mode == SILC_CHANNEL_UMODE_NONE) {
-      silc_server_command_send_status_reply(cmd, SILC_COMMAND_INVITE,
-                                           SILC_STATUS_ERR_NO_CHANNEL_PRIV);
-      goto out;
-    }
+  if (channel->mode & SILC_CHANNEL_MODE_INVITE &&
+      !(chl->mode & SILC_CHANNEL_UMODE_CHANOP) &&
+      !(chl->mode & SILC_CHANNEL_UMODE_CHANFO)) {
+    silc_server_command_send_status_reply(cmd, SILC_COMMAND_INVITE,
+                                         SILC_STATUS_ERR_NO_CHANNEL_PRIV,
+                                         0);
+    goto out;
   }
 
   /* Get destination client ID */
   tmp = silc_argument_get_arg_type(cmd->args, 2, &len);
   if (tmp) {
     char invite[512];
+    bool resolve;
 
-    dest_id = silc_id_payload_parse_id(tmp, len);
+    dest_id = silc_id_payload_parse_id(tmp, len, NULL);
     if (!dest_id) {
       silc_server_command_send_status_reply(cmd, SILC_COMMAND_INVITE,
-                                           SILC_STATUS_ERR_NO_CLIENT_ID);
+                                           SILC_STATUS_ERR_NO_CLIENT_ID, 0);
       goto out;
     }
 
     /* Get the client entry */
-    dest = silc_server_get_client_resolve(server, dest_id);
+    dest = silc_server_get_client_resolve(server, dest_id, FALSE, &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, 0);
        goto out;
       }
       
@@ -2429,27 +2567,29 @@ 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);
+                                           SILC_STATUS_ERR_USER_ON_CHANNEL,
+                                           0);
       goto out;
     }
     
     /* Get route to the client */
-    dest_sock = silc_server_get_client_route(server, NULL, 0, dest_id, &idata);
+    dest_sock = silc_server_get_client_route(server, NULL, 0, dest_id, 
+                                            &idata, NULL);
     if (!dest_sock) {
       silc_server_command_send_status_reply(cmd, SILC_COMMAND_INVITE,
-                                           SILC_STATUS_ERR_NO_SUCH_CLIENT_ID);
+                                           SILC_STATUS_ERR_NO_SUCH_CLIENT_ID,
+                                           0);
       goto out;
     }
 
@@ -2474,18 +2614,20 @@ SILC_SERVER_CMD_FUNC(invite)
     strncat(channel->invite_list, invite, len);
     strncat(channel->invite_list, ",", 1);
 
-    /* Send notify to the client that is invited to the channel */
-    idp = silc_id_payload_encode(channel_id, SILC_ID_CHANNEL);
-    idp2 = silc_id_payload_encode(sender->id, SILC_ID_CLIENT);
-    silc_server_send_notify_dest(server, dest_sock, FALSE, dest_id, 
-                                SILC_ID_CLIENT,
-                                SILC_NOTIFY_TYPE_INVITE, 3, 
-                                idp->data, idp->len, 
-                                channel->channel_name, 
-                                strlen(channel->channel_name),
-                                idp2->data, idp2->len);
-    silc_buffer_free(idp);
-    silc_buffer_free(idp2);
+    if (!(dest->mode & SILC_UMODE_BLOCK_INVITE)) {
+      /* Send notify to the client that is invited to the channel */
+      idp = silc_id_payload_encode(channel_id, SILC_ID_CHANNEL);
+      idp2 = silc_id_payload_encode(sender->id, SILC_ID_CLIENT);
+      silc_server_send_notify_dest(server, dest_sock, FALSE, dest_id, 
+                                  SILC_ID_CLIENT,
+                                  SILC_NOTIFY_TYPE_INVITE, 3, 
+                                  idp->data, idp->len, 
+                                  channel->channel_name, 
+                                  strlen(channel->channel_name),
+                                  idp2->data, idp2->len);
+      silc_buffer_free(idp);
+      silc_buffer_free(idp2);
+    }
   }
 
   /* Add the client to the invite list of the channel */
@@ -2542,7 +2684,7 @@ SILC_SERVER_CMD_FUNC(invite)
   if (add || del)
     packet = 
       silc_command_reply_payload_encode_va(SILC_COMMAND_INVITE,
-                                          SILC_STATUS_OK, ident, 2,
+                                          SILC_STATUS_OK, 0, ident, 2,
                                           2, tmp, len,
                                           3, channel->invite_list,
                                           channel->invite_list ?
@@ -2550,7 +2692,7 @@ SILC_SERVER_CMD_FUNC(invite)
   else
     packet = 
       silc_command_reply_payload_encode_va(SILC_COMMAND_INVITE,
-                                          SILC_STATUS_OK, ident, 1,
+                                          SILC_STATUS_OK, 0, ident, 1,
                                           2, tmp, len);
   silc_server_packet_send(server, cmd->sock, SILC_PACKET_COMMAND_REPLY, 0, 
                          packet->data, packet->len, FALSE);
@@ -2597,14 +2739,14 @@ 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);
 
   if (cmd->sock->type != SILC_SOCKET_TYPE_CLIENT)
     goto out;
 
-  /* Get destination ID */
+  /* Get message */
   tmp = silc_argument_get_arg_type(cmd->args, 1, &len);
   if (len > 128)
     tmp = NULL;
@@ -2616,8 +2758,8 @@ SILC_SERVER_CMD_FUNC(quit)
 
   /* We quit the connection with little timeout */
   silc_schedule_task_add(server->schedule, sock->sock,
-                    silc_server_command_quit_cb, (void *)q,
-                    0, 200000, SILC_TASK_TIMEOUT, SILC_TASK_PRI_LOW);
+                        silc_server_command_quit_cb, (void *)q,
+                        0, 200000, SILC_TASK_TIMEOUT, SILC_TASK_PRI_LOW);
 
  out:
   silc_server_command_free(cmd);
@@ -2634,7 +2776,8 @@ 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;
+  bool local;
 
   SILC_SERVER_COMMAND_CHECK(SILC_COMMAND_KILL, cmd, 1, 2);
 
@@ -2644,14 +2787,14 @@ SILC_SERVER_CMD_FUNC(kill)
   /* KILL command works only on router */
   if (server->server_type != SILC_ROUTER) {
     silc_server_command_send_status_reply(cmd, SILC_COMMAND_KILL,
-                                         SILC_STATUS_ERR_NO_ROUTER_PRIV);
+                                         SILC_STATUS_ERR_NO_ROUTER_PRIV, 0);
     goto out;
   }
 
   /* Check whether client has the permissions. */
   if (!(client->mode & SILC_UMODE_ROUTER_OPERATOR)) {
     silc_server_command_send_status_reply(cmd, SILC_COMMAND_KILL,
-                                         SILC_STATUS_ERR_NO_ROUTER_PRIV);
+                                         SILC_STATUS_ERR_NO_ROUTER_PRIV, 0);
     goto out;
   }
 
@@ -2659,25 +2802,30 @@ SILC_SERVER_CMD_FUNC(kill)
   tmp = silc_argument_get_arg_type(cmd->args, 1, &tmp_len);
   if (!tmp) {
     silc_server_command_send_status_reply(cmd, SILC_COMMAND_KILL,
-                                         SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
+                                         SILC_STATUS_ERR_NOT_ENOUGH_PARAMS,
+                                         0);
     goto out;
   }
-  client_id = silc_id_payload_parse_id(tmp, tmp_len);
+  client_id = silc_id_payload_parse_id(tmp, tmp_len, NULL);
   if (!client_id) {
     silc_server_command_send_status_reply(cmd, SILC_COMMAND_KILL,
-                                         SILC_STATUS_ERR_NO_SUCH_CLIENT_ID);
+                                         SILC_STATUS_ERR_NO_SUCH_CLIENT_ID,
+                                         0);
     goto out;
   }
 
   /* Get the client entry */
   remote_client = silc_idlist_find_client_by_id(server->local_list, 
                                                client_id, TRUE, NULL);
+  local = TRUE;
   if (!remote_client) {
     remote_client = silc_idlist_find_client_by_id(server->global_list, 
                                                  client_id, TRUE, NULL);
+    local = FALSE;
     if (!remote_client) {
       silc_server_command_send_status_reply(cmd, SILC_COMMAND_KILL,
-                                           SILC_STATUS_ERR_NO_SUCH_CLIENT_ID);
+                                           SILC_STATUS_ERR_NO_SUCH_CLIENT_ID,
+                                           0);
       goto out;
     }
   }
@@ -2685,53 +2833,20 @@ 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,
-                                       SILC_STATUS_OK);
-
-  /* Send the KILL notify packets. First send it to the channel, then
-     to our primary router and then directly to the client who is being
-     killed right now. */
-
-  /* Send KILLED notify to the channels. It is not sent to the client
-     as it will be sent differently destined directly to the client and not
-     to the channel. */
-  silc_server_send_notify_on_channels(server, remote_client, 
-                                     remote_client, SILC_NOTIFY_TYPE_KILLED,
-                                     comment ? 2 : 1,
-                                     tmp, tmp_len,
-                                     comment, comment ? tmp_len2 : 0);
-
-  /* Send KILLED notify to primary route */
-  if (!server->standalone)
-    silc_server_send_notify_killed(server, server->router->connection, TRUE,
-                                  remote_client->id, comment);
-
-  /* Send KILLED notify to the client directly */
-  silc_server_send_notify_killed(server, remote_client->connection ? 
-                                remote_client->connection : 
-                                remote_client->router->connection, FALSE,
-                                remote_client->id, comment);
-
-  /* Remove the client from all channels. This generates new keys to the
-     channels as well. */
-  silc_server_remove_from_channels(server, NULL, remote_client, FALSE, 
-                                  NULL, TRUE);
-
-  /* Remove the client entry, If it is locally connected then we will also
-     disconnect the client here */
-  if (remote_client->connection) {
-    /* Remove locally conneted client */
-    SilcSocketConnection sock = remote_client->connection;
-    silc_server_free_client_data(server, sock, remote_client, FALSE, NULL);
-    silc_server_close_connection(server, sock);
-  } else {
-    /* Remove remote client */
-    if (!silc_idlist_del_client(server->global_list, remote_client))
-      silc_idlist_del_client(server->local_list, remote_client);
-  }
+                                       SILC_STATUS_OK, 0);
+
+  /* Check if anyone is watching this nickname */
+  if (server->server_type == SILC_ROUTER)
+    silc_server_check_watcher_list(server, client, NULL,
+                                  SILC_NOTIFY_TYPE_KILLED);
+
+  /* Now do the killing */
+  silc_server_kill_client(server, remote_client, comment, client->id,
+                         SILC_ID_CLIENT);
 
  out:
   silc_server_command_free(cmd);
@@ -2747,9 +2862,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;
 
@@ -2761,10 +2876,10 @@ 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);
+                                           SILC_STATUS_ERR_NO_SERVER_ID, 0);
       goto out;
     }
   }
@@ -2778,7 +2893,8 @@ SILC_SERVER_CMD_FUNC(info)
                                            server_id, TRUE, NULL);
       if (!entry && server->server_type != SILC_SERVER) {
        silc_server_command_send_status_reply(cmd, SILC_COMMAND_INFO,
-                                             SILC_STATUS_ERR_NO_SUCH_SERVER);
+                                             SILC_STATUS_ERR_NO_SUCH_SERVER,
+                                             0);
        goto out;
       }
     }
@@ -2798,10 +2914,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;
@@ -2820,10 +2936,10 @@ 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, silc_rng_get_rn16(server->rng));
+      silc_command_set_ident(cmd->payload, ++server->cmd_ident);
       tmpbuf = silc_command_payload_encode_payload(cmd->payload);
 
       silc_server_packet_send(server, entry->connection,
@@ -2833,22 +2949,21 @@ 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, silc_rng_get_rn16(server->rng));
+      silc_command_set_ident(cmd->payload, ++server->cmd_ident);
       tmpbuf = silc_command_payload_encode_payload(cmd->payload);
 
       silc_server_packet_send(server, server->router->connection,
@@ -2858,13 +2973,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;
     }
   }
 
@@ -2872,7 +2986,7 @@ SILC_SERVER_CMD_FUNC(info)
 
   if (!entry) {
     silc_server_command_send_status_reply(cmd, SILC_COMMAND_INFO,
-                                         SILC_STATUS_ERR_NO_SUCH_SERVER);
+                                         SILC_STATUS_ERR_NO_SUCH_SERVER, 0);
     goto out;
   }
 
@@ -2882,20 +2996,14 @@ SILC_SERVER_CMD_FUNC(info)
   server_name = entry->server_name;
 
   /* Send the reply */
-  if (server_info)
-    packet = silc_command_reply_payload_encode_va(SILC_COMMAND_INFO,
-                                                 SILC_STATUS_OK, ident, 3,
-                                                 2, idp->data, idp->len,
-                                                 3, server_name, 
-                                                 strlen(server_name),
-                                                 4, server_info, 
-                                                 strlen(server_info));
-  else
-    packet = silc_command_reply_payload_encode_va(SILC_COMMAND_INFO,
-                                                 SILC_STATUS_OK, ident, 2,
-                                                 2, idp->data, idp->len,
-                                                 3, server_name, 
-                                                 strlen(server_name));
+  packet = silc_command_reply_payload_encode_va(SILC_COMMAND_INFO,
+                                               SILC_STATUS_OK, 0, ident, 3,
+                                               2, idp->data, idp->len,
+                                               3, server_name, 
+                                               strlen(server_name),
+                                               4, server_info, 
+                                               server_info ? 
+                                               strlen(server_info) : 0);
   silc_server_packet_send(server, cmd->sock, SILC_PACKET_COMMAND_REPLY, 0, 
                          packet->data, packet->len, FALSE);
     
@@ -2913,7 +3021,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);
@@ -2922,7 +3030,7 @@ SILC_SERVER_CMD_FUNC(ping)
   tmp = silc_argument_get_arg_type(cmd->args, 1, &len);
   if (!tmp) {
     silc_server_command_send_status_reply(cmd, SILC_COMMAND_PING,
-                                         SILC_STATUS_ERR_NO_SERVER_ID);
+                                         SILC_STATUS_ERR_NO_SERVER_ID, 0);
     goto out;
   }
   id = silc_id_str2id(tmp, len, SILC_ID_SERVER);
@@ -2932,10 +3040,10 @@ SILC_SERVER_CMD_FUNC(ping)
   if (SILC_ID_SERVER_COMPARE(id, server->id)) {
     /* Send our reply */
     silc_server_command_send_status_reply(cmd, SILC_COMMAND_PING,
-                                         SILC_STATUS_OK);
+                                         SILC_STATUS_OK, 0);
   } else {
     silc_server_command_send_status_reply(cmd, SILC_COMMAND_PING,
-                                         SILC_STATUS_ERR_NO_SUCH_SERVER);
+                                         SILC_STATUS_ERR_NO_SUCH_SERVER, 0);
     goto out;
   }
 
@@ -2945,6 +3053,102 @@ SILC_SERVER_CMD_FUNC(ping)
   silc_server_command_free(cmd);
 }
 
+/* Server side of command STATS. */
+
+SILC_SERVER_CMD_FUNC(stats)
+{
+  SilcServerCommandContext cmd = (SilcServerCommandContext)context;
+  SilcServer server = cmd->server;
+  SilcServerID *server_id;
+  unsigned char *tmp;
+  SilcUInt32 tmp_len;
+  SilcBuffer packet, stats;
+  SilcUInt16 ident = silc_command_get_ident(cmd->payload);
+  SilcUInt32 uptime;
+
+  SILC_SERVER_COMMAND_CHECK(SILC_COMMAND_STATS, cmd, 1, 1);
+
+  /* Get Server ID */
+  tmp = silc_argument_get_arg_type(cmd->args, 1, &tmp_len);
+  if (!tmp) {
+    silc_server_command_send_status_reply(cmd, SILC_COMMAND_INFO,
+                                         SILC_STATUS_ERR_NO_SERVER_ID, 0);
+    goto out;
+  }
+  server_id = silc_id_payload_parse_id(tmp, tmp_len, NULL);
+  if (!server_id)
+    goto out;
+
+  /* The ID must be ours */
+  if (!SILC_ID_SERVER_COMPARE(server->id, server_id)) {
+    silc_server_command_send_status_reply(cmd, SILC_COMMAND_INFO,
+                                         SILC_STATUS_ERR_NO_SUCH_SERVER, 0);
+    silc_free(server_id);
+    goto out;
+  }
+  silc_free(server_id);
+
+  /* If we are router then just send everything we got. If we are normal
+     server then we'll send this to our router to get all the latest
+     statistical information. */
+  if (!cmd->pending && server->server_type != SILC_ROUTER && 
+      !server->standalone) {
+    /* Send request to our router */
+    SilcBuffer idp = silc_id_payload_encode(server->router->id, 
+                                           SILC_ID_SERVER);
+    packet = silc_command_payload_encode_va(SILC_COMMAND_STATS, 
+                                           ++server->cmd_ident, 1,
+                                           1, idp->data, idp->len);
+    silc_server_packet_send(server, server->router->connection,
+                           SILC_PACKET_COMMAND, 0, packet->data,
+                           packet->len, FALSE);
+
+    /* Reprocess this packet after received reply from router */
+    silc_server_command_pending(server, SILC_COMMAND_STATS, 
+                               server->cmd_ident,
+                               silc_server_command_stats,
+                               silc_server_command_dup(cmd));
+    cmd->pending = TRUE;
+    silc_buffer_free(packet);
+    silc_buffer_free(idp);
+    goto out;
+  }
+
+  /* Send our reply to sender */
+  uptime = time(NULL) - server->starttime;
+
+  stats = silc_buffer_alloc_size(60);
+  silc_buffer_format(stats,
+                    SILC_STR_UI_INT(server->starttime),
+                    SILC_STR_UI_INT(uptime),
+                    SILC_STR_UI_INT(server->stat.my_clients),
+                    SILC_STR_UI_INT(server->stat.my_channels),
+                    SILC_STR_UI_INT(server->stat.my_server_ops),
+                    SILC_STR_UI_INT(server->stat.my_router_ops),
+                    SILC_STR_UI_INT(server->stat.cell_clients),
+                    SILC_STR_UI_INT(server->stat.cell_channels),
+                    SILC_STR_UI_INT(server->stat.cell_servers),
+                    SILC_STR_UI_INT(server->stat.clients),
+                    SILC_STR_UI_INT(server->stat.channels),
+                    SILC_STR_UI_INT(server->stat.servers),
+                    SILC_STR_UI_INT(server->stat.routers),
+                    SILC_STR_UI_INT(server->stat.server_ops),
+                    SILC_STR_UI_INT(server->stat.router_ops),
+                    SILC_STR_END);
+
+  packet = silc_command_reply_payload_encode_va(SILC_COMMAND_STATS, 
+                                               SILC_STATUS_OK, 0, ident, 2,
+                                               2, tmp, tmp_len,
+                                               3, stats->data, stats->len);
+  silc_server_packet_send(server, cmd->sock, SILC_PACKET_COMMAND_REPLY,
+                         0, packet->data, packet->len, FALSE);
+  silc_buffer_free(packet);
+  silc_buffer_free(stats);
+
+ out:
+  silc_server_command_free(cmd);
+}
+
 /* Internal routine to join channel. The channel sent to this function
    has been either created or resolved from ID lists. This joins the sent
    client to the channel. */
@@ -2955,17 +3159,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,
+                                            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"));
 
@@ -2976,96 +3184,127 @@ 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, FALSE, 
+                                           &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, 0);
+       goto out;
+      }
+
       /* The client info is being resolved. Reprocess this packet after
         receiving the reply to the query. */
       silc_server_command_pending(server, SILC_COMMAND_WHOIS, 
-                                 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;
   }
 
+  /*
+   * Check founder auth payload if provided.  If client can gain founder
+   * privileges it can override various conditions on joining the channel,
+   * and can have directly the founder mode set on the channel.
+   */
+  if (auth && auth_len && channel->mode & SILC_CHANNEL_MODE_FOUNDER_AUTH) {
+    SilcIDListData idata = (SilcIDListData)client;
+
+    if (channel->founder_key && idata->public_key &&
+       silc_pkcs_public_key_compare(channel->founder_key, 
+                                    idata->public_key)) {
+      /* Check whether the client is to become founder */
+      if (silc_auth_verify_data(auth, auth_len, SILC_AUTH_PUBLIC_KEY,
+                               channel->founder_key, 0,
+                               idata->hash, client->id, SILC_ID_CLIENT)) {
+       umode = (SILC_CHANNEL_UMODE_CHANOP | SILC_CHANNEL_UMODE_CHANFO);
+       founder = TRUE;
+      }
+    }
+  }
+
   /*
    * Check channel modes
    */
 
-  memset(check, 0, sizeof(check));
-  memset(check2, 0, sizeof(check2));
-  strncat(check, client->nickname, strlen(client->nickname));
-  strncat(check, "!", 1);
-  strncat(check, client->username, strlen(client->username));
-  if (!strchr(client->username, '@')) {
-    strncat(check, "@", 1);
-    strncat(check, cmd->sock->hostname, strlen(cmd->sock->hostname));
-  }
-
-  strncat(check2, client->nickname, strlen(client->nickname));
-  if (!strchr(client->nickname, '@')) {
-    strncat(check2, "@", 1);
-    strncat(check2, server->server_name, strlen(server->server_name));
-  }
-  strncat(check2, "!", 1);
-  strncat(check2, client->username, strlen(client->username));
-  if (!strchr(client->username, '@')) {
-    strncat(check2, "@", 1);
-    strncat(check2, cmd->sock->hostname, strlen(cmd->sock->hostname));
-  }
-
-  /* Check invite list if channel is invite-only channel */
-  if (channel->mode & SILC_CHANNEL_MODE_INVITE) {
-    if (!channel->invite_list ||
-       (!silc_string_match(channel->invite_list, check) &&
-        !silc_string_match(channel->invite_list, check2))) {
-      silc_server_command_send_status_reply(cmd, SILC_COMMAND_JOIN,
-                                           SILC_STATUS_ERR_NOT_INVITED);
-      goto out;
+  if (!umode) {
+    memset(check, 0, sizeof(check));
+    memset(check2, 0, sizeof(check2));
+    strncat(check, client->nickname, strlen(client->nickname));
+    strncat(check, "!", 1);
+    strncat(check, client->username, strlen(client->username));
+    if (!strchr(client->username, '@')) {
+      strncat(check, "@", 1);
+      strncat(check, cmd->sock->hostname, strlen(cmd->sock->hostname));
     }
-  }
 
-  /* Check ban list if it exists. If the client's nickname, server,
-     username and/or hostname is in the ban list the access to the
-     channel is denied. */
-  if (channel->ban_list) {
-    if (silc_string_match(channel->ban_list, check) ||
-       silc_string_match(channel->ban_list, check2)) {
-      silc_server_command_send_status_reply(cmd, SILC_COMMAND_JOIN,
-                             SILC_STATUS_ERR_BANNED_FROM_CHANNEL);
-      goto out;
+    strncat(check2, client->nickname, strlen(client->nickname));
+    if (!strchr(client->nickname, '@')) {
+      strncat(check2, "@", 1);
+      strncat(check2, server->server_name, strlen(server->server_name));
+    }
+    strncat(check2, "!", 1);
+    strncat(check2, client->username, strlen(client->username));
+    if (!strchr(client->username, '@')) {
+      strncat(check2, "@", 1);
+      strncat(check2, cmd->sock->hostname, strlen(cmd->sock->hostname));
+    }
+    
+    /* Check invite list if channel is invite-only channel */
+    if (channel->mode & SILC_CHANNEL_MODE_INVITE) {
+      if (!channel->invite_list ||
+         (!silc_string_match(channel->invite_list, check) &&
+          !silc_string_match(channel->invite_list, check2))) {
+       silc_server_command_send_status_reply(cmd, SILC_COMMAND_JOIN,
+                                             SILC_STATUS_ERR_NOT_INVITED, 0);
+       goto out;
+      }
     }
-  }
 
-  /* Get passphrase */
-  tmp = silc_argument_get_arg_type(cmd->args, 3, &tmp_len);
-  if (tmp) {
-    passphrase = silc_calloc(tmp_len, sizeof(*passphrase));
-    memcpy(passphrase, tmp, tmp_len);
-  }
-  
-  /* Check the channel passphrase if set. */
-  if (channel->mode & SILC_CHANNEL_MODE_PASSPHRASE) {
-    if (!passphrase || memcmp(channel->passphrase, passphrase,
-                             strlen(channel->passphrase))) {
-      silc_server_command_send_status_reply(cmd, SILC_COMMAND_JOIN,
-                                           SILC_STATUS_ERR_BAD_PASSWORD);
-      goto out;
+    /* Check ban list if it exists. If the client's nickname, server,
+       username and/or hostname is in the ban list the access to the
+       channel is denied. */
+    if (channel->ban_list) {
+      if (silc_string_match(channel->ban_list, check) ||
+         silc_string_match(channel->ban_list, check2)) {
+       silc_server_command_send_status_reply(
+                                     cmd, SILC_COMMAND_JOIN,
+                                     SILC_STATUS_ERR_BANNED_FROM_CHANNEL, 0);
+       goto out;
+      }
+    }
+    
+    /* Check user count limit if set. */
+    if (channel->mode & SILC_CHANNEL_MODE_ULIMIT) {
+      if (silc_hash_table_count(channel->user_list) + 1 > 
+         channel->user_limit) {
+       silc_server_command_send_status_reply(cmd, SILC_COMMAND_JOIN,
+                                             SILC_STATUS_ERR_CHANNEL_IS_FULL,
+                                             0);
+       goto out;
+      }
     }
   }
 
-  /* Check user count limit if set. */
-  if (channel->mode & SILC_CHANNEL_MODE_ULIMIT) {
-    if (silc_hash_table_count(channel->user_list) + 1 > 
-       channel->user_limit) {
+  /* Check the channel passphrase if set. */
+  if (channel->mode & SILC_CHANNEL_MODE_PASSPHRASE) {
+    /* Get passphrase */
+    tmp = silc_argument_get_arg_type(cmd->args, 3, &tmp_len);
+    if (tmp)
+      passphrase = silc_memdup(tmp, tmp_len);
+  
+    if (!passphrase || !channel->passphrase ||
+        memcmp(passphrase, channel->passphrase, strlen(channel->passphrase))) {
       silc_server_command_send_status_reply(cmd, SILC_COMMAND_JOIN,
-                                           SILC_STATUS_ERR_CHANNEL_IS_FULL);
+                                           SILC_STATUS_ERR_BAD_PASSWORD, 0);
       goto out;
     }
   }
@@ -3075,9 +3314,9 @@ 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);
+                                         SILC_STATUS_ERR_USER_ON_CHANNEL, 0);
     goto out;
   }
 
@@ -3103,6 +3342,8 @@ 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++;
+  channel->disabled = FALSE;
 
   /* Get users on the channel */
   silc_server_get_users_on_channel(server, channel, &user_list, &mode_list,
@@ -3119,7 +3360,9 @@ static void silc_server_command_join_channel(SilcServer server,
 
   if (!(channel->mode & SILC_CHANNEL_MODE_PRIVKEY)) {
     tmp = silc_id_id2str(channel->id, SILC_ID_CHANNEL);
-    keyp = silc_channel_key_payload_encode(SILC_ID_CHANNEL_LEN, tmp, 
+    keyp = silc_channel_key_payload_encode(silc_id_get_len(channel->id,
+                                                          SILC_ID_CHANNEL), 
+                                          tmp,
                                           strlen(channel->channel_key->
                                                  cipher->name),
                                           channel->channel_key->cipher->name,
@@ -3129,7 +3372,7 @@ static void silc_server_command_join_channel(SilcServer server,
 
   reply = 
     silc_command_reply_payload_encode_va(SILC_COMMAND_JOIN,
-                                        SILC_STATUS_OK, ident, 13,
+                                        SILC_STATUS_OK, 0, ident, 13,
                                         2, channel->channel_name,
                                         strlen(channel->channel_name),
                                         3, chidp->data, chidp->len,
@@ -3165,7 +3408,7 @@ static void silc_server_command_join_channel(SilcServer server,
      we'll ignore it (in packet_receive.c) so we must send it here. If
      we are router then this will send it to local clients and local
      servers. */
-  silc_server_send_notify_to_channel(server, NULL, channel, FALSE,
+  silc_server_send_notify_to_channel(server, NULL, channel, FALSE, 
                                     SILC_NOTIFY_TYPE_JOIN, 2,
                                     clidp->data, clidp->len,
                                     chidp->data, chidp->len);
@@ -3183,6 +3426,24 @@ static void silc_server_command_join_channel(SilcServer server,
                              keyp->data, keyp->len, FALSE, TRUE);
   }
 
+  /* If client became founder by providing correct founder auth data
+     notify the mode change to the channel. */
+  if (founder) {
+    SILC_PUT32_MSB(chl->mode, mode);
+    silc_server_send_notify_to_channel(server, NULL, channel, FALSE, 
+                                      SILC_NOTIFY_TYPE_CUMODE_CHANGE, 3,
+                                      clidp->data, clidp->len,
+                                      mode, 4, clidp->data, clidp->len);
+      
+    /* Set CUMODE notify type to network */
+    if (!server->standalone)
+      silc_server_send_notify_cumode(server, server->router->connection,
+                                    server->server_type == SILC_ROUTER ? 
+                                    TRUE : FALSE, channel,
+                                    chl->mode, client->id, SILC_ID_CLIENT,
+                                    client->id);
+  }
+
   silc_buffer_free(reply);
   silc_buffer_free(clidp);
   silc_buffer_free(chidp);
@@ -3201,30 +3462,32 @@ SILC_SERVER_CMD_FUNC(join)
 {
   SilcServerCommandContext cmd = (SilcServerCommandContext)context;
   SilcServer server = cmd->server;
-  uint32 tmp_len;
+  unsigned char *auth;
+  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;
 
-  SILC_SERVER_COMMAND_CHECK(SILC_COMMAND_JOIN, cmd, 1, 4);
+  SILC_SERVER_COMMAND_CHECK(SILC_COMMAND_JOIN, cmd, 2, 6);
 
   /* Get channel name */
   tmp = silc_argument_get_arg_type(cmd->args, 1, &tmp_len);
   if (!tmp) {
     silc_server_command_send_status_reply(cmd, SILC_COMMAND_JOIN,
-                                         SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
+                                         SILC_STATUS_ERR_NOT_ENOUGH_PARAMS,
+                                         0);
     goto out;
   }
   channel_name = tmp;
 
-  if (strlen(channel_name) > 256)
+  if (tmp_len > 256)
     channel_name[255] = '\0';
 
-  if (silc_server_command_bad_chars(channel_name) == TRUE) {
+  if (silc_server_name_bad_chars(channel_name, tmp_len) == TRUE) {
     silc_server_command_send_status_reply(cmd, SILC_COMMAND_JOIN,
-                                         SILC_STATUS_ERR_BAD_CHANNEL);
+                                         SILC_STATUS_ERR_BAD_CHANNEL, 0);
     goto out;
   }
 
@@ -3232,37 +3495,33 @@ SILC_SERVER_CMD_FUNC(join)
   tmp = silc_argument_get_arg_type(cmd->args, 2, &tmp_len);
   if (!tmp) {
     silc_server_command_send_status_reply(cmd, SILC_COMMAND_JOIN,
-                                         SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
+                                         SILC_STATUS_ERR_NOT_ENOUGH_PARAMS,
+                                         0);
     goto out;
   }
-  client_id = silc_id_payload_parse_id(tmp, tmp_len);
+  client_id = silc_id_payload_parse_id(tmp, tmp_len, NULL);
   if (!client_id) {
     silc_server_command_send_status_reply(cmd, SILC_COMMAND_JOIN,
-                                         SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
+                                         SILC_STATUS_ERR_NOT_ENOUGH_PARAMS,
+                                         0);
     goto out;
   }
 
-  /* Get cipher and hmac name */
+  /* Get cipher, hmac name and auth payload */
   cipher = silc_argument_get_arg_type(cmd->args, 4, NULL);
   hmac = silc_argument_get_arg_type(cmd->args, 5, NULL);
+  auth = silc_argument_get_arg_type(cmd->args, 6, &auth_len);
 
   /* See if the channel exists */
   channel = silc_idlist_find_channel_by_name(server->local_list, 
                                             channel_name, NULL);
 
   if (cmd->sock->type == SILC_SOCKET_TYPE_CLIENT) {
-    /* If this is coming from client the Client ID in the command packet must
-       be same as the client's ID. */
-    if (cmd->sock->type == SILC_SOCKET_TYPE_CLIENT) {
-      SilcClientEntry entry = (SilcClientEntry)cmd->sock->user_data;
-      if (!SILC_ID_CLIENT_COMPARE(entry->id, client_id)) {
-       silc_server_command_send_status_reply(cmd, SILC_COMMAND_JOIN,
-                                       SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
-       goto out;
-      }
-    }
+    SilcClientEntry entry = (SilcClientEntry)cmd->sock->user_data;
+    client_id = silc_id_dup(entry->id, SILC_ID_CLIENT);
 
-    if (!channel || !channel->id) {
+    if (!channel || 
+       (channel->disabled && server->server_type != SILC_ROUTER)) {
       /* Channel not found */
 
       /* If we are standalone server we don't have a router, we just create 
@@ -3271,8 +3530,10 @@ SILC_SERVER_CMD_FUNC(join)
        channel = silc_server_create_new_channel(server, server->id, cipher, 
                                                 hmac, channel_name, TRUE);
        if (!channel) {
-         silc_server_command_send_status_reply(cmd, SILC_COMMAND_JOIN,
-                                       SILC_STATUS_ERR_UNKNOWN_ALGORITHM);
+         silc_server_command_send_status_reply(
+                                        cmd, SILC_COMMAND_JOIN,
+                                        SILC_STATUS_ERR_UNKNOWN_ALGORITHM,
+                                        0);
          goto out;
        }
        
@@ -3288,7 +3549,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
@@ -3297,7 +3558,7 @@ SILC_SERVER_CMD_FUNC(join)
            goto out;
          
          old_ident = silc_command_get_ident(cmd->payload);
-         silc_command_set_ident(cmd->payload, silc_rng_get_rn16(server->rng));
+         silc_command_set_ident(cmd->payload, ++server->cmd_ident);
          tmpbuf = silc_command_payload_encode_payload(cmd->payload);
          
          /* Send JOIN command to our router */
@@ -3309,11 +3570,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
@@ -3325,8 +3587,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, 0);
            goto out;
          }
 
@@ -3356,8 +3619,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, 0);
          goto out;
        }
 
@@ -3370,23 +3634,30 @@ SILC_SERVER_CMD_FUNC(join)
 
   /* Check whether the channel was created by our router */
   if (cmd->pending && context2) {
-    SilcServerCommandReplyContext reply = 
-      (SilcServerCommandReplyContext)context2;
+    SilcServerCommandReplyContext reply = context2;
+
     if (silc_command_get(reply->payload) == SILC_COMMAND_JOIN) {
       tmp = silc_argument_get_arg_type(reply->args, 6, NULL);
       SILC_GET32_MSB(created, tmp);
-      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
      will be the channel founder and operator. */
-  if (!channel->global_users && !silc_hash_table_count(channel->user_list))
+  if (!channel->disabled &&
+      !channel->global_users && !silc_hash_table_count(channel->user_list))
     umode = (SILC_CHANNEL_UMODE_CHANOP | SILC_CHANNEL_UMODE_CHANFO);
 
   /* Join to the channel */
   silc_server_command_join_channel(server, cmd, channel, client_id,
-                                  created, create_key, umode);
+                                  created, create_key, umode,
+                                  auth, auth_len);
 
   silc_free(client_id);
 
@@ -3403,8 +3674,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);
 
@@ -3412,7 +3683,7 @@ SILC_SERVER_CMD_FUNC(motd)
   dest_server = silc_argument_get_arg_type(cmd->args, 1, NULL);
   if (!dest_server) {
     silc_server_command_send_status_reply(cmd, SILC_COMMAND_MOTD,
-                                         SILC_STATUS_ERR_NO_SUCH_SERVER);
+                                         SILC_STATUS_ERR_NO_SUCH_SERVER, 0);
     goto out;
   }
 
@@ -3421,23 +3692,24 @@ 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;
       
       motd[motd_len] = 0;
       packet = silc_command_reply_payload_encode_va(SILC_COMMAND_MOTD,
-                                                   SILC_STATUS_OK, ident, 2,
+                                                   SILC_STATUS_OK, 0, 
+                                                   ident, 2,
                                                    2, idp, idp->len,
                                                    3, motd, motd_len);
-      goto out;
     } else {
       /* No motd */
       packet = silc_command_reply_payload_encode_va(SILC_COMMAND_MOTD,
-                                                   SILC_STATUS_OK, ident, 1,
+                                                   SILC_STATUS_OK, 0, 
+                                                   ident, 1,
                                                    2, idp, idp->len);
     }
 
@@ -3460,10 +3732,10 @@ 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, silc_rng_get_rn16(server->rng));
+      silc_command_set_ident(cmd->payload, ++server->cmd_ident);
       tmpbuf = silc_command_payload_encode_payload(cmd->payload);
 
       silc_server_packet_send(server, entry->connection,
@@ -3473,22 +3745,21 @@ 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, silc_rng_get_rn16(server->rng));
+      silc_command_set_ident(cmd->payload, ++server->cmd_ident);
       tmpbuf = silc_command_payload_encode_payload(cmd->payload);
 
       silc_server_packet_send(server, server->router->connection,
@@ -3498,34 +3769,27 @@ 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) {
       silc_server_command_send_status_reply(cmd, SILC_COMMAND_INFO,
-                                           SILC_STATUS_ERR_NO_SUCH_SERVER);
+                                           SILC_STATUS_ERR_NO_SUCH_SERVER, 0);
       goto out;
     }
 
     idp = silc_id_payload_encode(server->id_entry->id, SILC_ID_SERVER);
-
-    if (entry->motd)
-      packet = silc_command_reply_payload_encode_va(SILC_COMMAND_MOTD,
-                                                   SILC_STATUS_OK, ident, 2,
-                                                   2, idp, idp->len,
-                                                   3, entry->motd,
-                                                   strlen(entry->motd));
-    else
-      packet = silc_command_reply_payload_encode_va(SILC_COMMAND_MOTD,
-                                                   SILC_STATUS_OK, ident, 1,
-                                                   2, idp, idp->len);
-
+    packet = silc_command_reply_payload_encode_va(SILC_COMMAND_MOTD,
+                                                 SILC_STATUS_OK, 0, ident, 2,
+                                                 2, idp, idp->len,
+                                                 3, entry->motd,
+                                                 entry->motd ? 
+                                                 strlen(entry->motd) : 0);
     silc_server_packet_send(server, cmd->sock, SILC_PACKET_COMMAND_REPLY, 0, 
                            packet->data, packet->len, FALSE);
     silc_buffer_free(packet);
@@ -3546,71 +3810,65 @@ SILC_SERVER_CMD_FUNC(umode)
   SilcServer server = cmd->server;
   SilcClientEntry client = (SilcClientEntry)cmd->sock->user_data;
   SilcBuffer packet;
-  unsigned char *tmp_mask;
-  uint32 mask;
-  uint16 ident = silc_command_get_ident(cmd->payload);
+  unsigned char *tmp_mask, m[4];
+  SilcUInt32 mask = 0;
+  SilcUInt16 ident = silc_command_get_ident(cmd->payload);
+  bool set_mask = FALSE;
 
   if (cmd->sock->type != SILC_SOCKET_TYPE_CLIENT)
     goto out;
 
-  SILC_SERVER_COMMAND_CHECK(SILC_COMMAND_UMODE, cmd, 2, 2);
+  SILC_SERVER_COMMAND_CHECK(SILC_COMMAND_UMODE, cmd, 1, 2);
 
   /* Get the client's mode mask */
   tmp_mask = silc_argument_get_arg_type(cmd->args, 2, NULL);
-  if (!tmp_mask) {
-    silc_server_command_send_status_reply(cmd, SILC_COMMAND_UMODE,
-                                         SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
-    goto out;
+  if (tmp_mask) {
+    SILC_GET32_MSB(mask, tmp_mask);
+    set_mask = TRUE;
   }
-  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 */
+  if (set_mask) {
+    /* Check that mode changing is allowed. */
+    if (!silc_server_check_umode_rights(server, client, mask)) {
       silc_server_command_send_status_reply(cmd, SILC_COMMAND_UMODE,
-                                           SILC_STATUS_ERR_PERM_DENIED);
+                                           SILC_STATUS_ERR_PERM_DENIED, 0);
       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;
+    /* Anonymous mode cannot be set by client */
+    if (mask & SILC_UMODE_ANONYMOUS) {
+      if (!(client->mode & SILC_UMODE_ANONYMOUS)) {
+       silc_server_command_send_status_reply(cmd, SILC_COMMAND_UMODE,
+                                             SILC_STATUS_ERR_PERM_DENIED, 0);
+       goto out;
+      }
+    } else {
+      if (client->mode & SILC_UMODE_ANONYMOUS) {
+       silc_server_command_send_status_reply(cmd, SILC_COMMAND_UMODE,
+                                             SILC_STATUS_ERR_PERM_DENIED, 0);
+       goto out;
+      }
     }
-  } else {
-    if (client->mode & SILC_UMODE_ROUTER_OPERATOR)
-      /* Remove the router operator rights */
-      client->mode &= ~SILC_UMODE_ROUTER_OPERATOR;
-  }
 
-  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)
-    silc_server_send_notify_umode(server, server->router->connection, TRUE,
-                                 client->id, client->mode);
+    /* Send UMODE change to primary router */
+    if (!server->standalone)
+      silc_server_send_notify_umode(server, server->router->connection, TRUE,
+                                   client->id, client->mode);
+
+    /* Check if anyone is watching this nickname */
+    if (server->server_type == SILC_ROUTER)
+      silc_server_check_watcher_list(server, client, NULL,
+                                    SILC_NOTIFY_TYPE_UMODE_CHANGE);
+  }
 
   /* Send command reply to sender */
+  SILC_PUT32_MSB(client->mode, m);
   packet = silc_command_reply_payload_encode_va(SILC_COMMAND_UMODE,
-                                               SILC_STATUS_OK, ident, 1,
-                                               2, tmp_mask, 4);
+                                               SILC_STATUS_OK, 0, ident, 1,
+                                               2, m, sizeof(m));
   silc_server_packet_send(server, cmd->sock, SILC_PACKET_COMMAND_REPLY, 0, 
                          packet->data, packet->len, FALSE);
   silc_buffer_free(packet);
@@ -3619,75 +3877,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)
@@ -3696,39 +3885,41 @@ SILC_SERVER_CMD_FUNC(cmode)
   SilcServer server = cmd->server;
   SilcClientEntry client = (SilcClientEntry)cmd->sock->user_data;
   SilcIDListData idata = (SilcIDListData)client;
-  SilcChannelID *channel_id;
+  SilcChannelID *channel_id = NULL;
   SilcChannelEntry channel;
   SilcChannelClientEntry chl;
   SilcBuffer packet, cidp;
   unsigned char *tmp, *tmp_id, *tmp_mask;
-  char *cipher = NULL, *hmac = NULL;
-  uint32 mode_mask, tmp_len, tmp_len2;
-  uint16 ident = silc_command_get_ident(cmd->payload);
+  char *cipher = NULL, *hmac = NULL, *passphrase = NULL;
+  SilcUInt32 mode_mask = 0, tmp_len, tmp_len2;
+  SilcUInt16 ident = silc_command_get_ident(cmd->payload);
+  bool set_mask = FALSE;
+  SilcPublicKey founder_key = NULL;
+  unsigned char *fkey = NULL;
+  SilcUInt32 fkey_len = 0;
 
-  SILC_SERVER_COMMAND_CHECK(SILC_COMMAND_CMODE, cmd, 2, 7);
+  SILC_SERVER_COMMAND_CHECK(SILC_COMMAND_CMODE, cmd, 1, 7);
 
   /* Get Channel ID */
   tmp_id = silc_argument_get_arg_type(cmd->args, 1, &tmp_len2);
   if (!tmp_id) {
     silc_server_command_send_status_reply(cmd, SILC_COMMAND_CMODE,
-                                         SILC_STATUS_ERR_NO_CHANNEL_ID);
+                                         SILC_STATUS_ERR_NO_CHANNEL_ID, 0);
     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);
+                                         SILC_STATUS_ERR_NO_CHANNEL_ID, 0);
     goto out;
   }
 
   /* Get the channel mode mask */
   tmp_mask = silc_argument_get_arg_type(cmd->args, 2, &tmp_len);
-  if (!tmp_mask) {
-    silc_server_command_send_status_reply(cmd, SILC_COMMAND_CMODE,
-                                         SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
-    goto out;
+  if (tmp_mask) {
+    SILC_GET32_MSB(mode_mask, tmp_mask);
+    set_mask = TRUE;
   }
-  SILC_GET32_MSB(mode_mask, tmp_mask);
 
   /* Get channel entry */
   channel = silc_idlist_find_channel_by_id(server->local_list, 
@@ -3738,25 +3929,42 @@ SILC_SERVER_CMD_FUNC(cmode)
                                             channel_id, NULL);
     if (!channel) {
       silc_server_command_send_status_reply(cmd, SILC_COMMAND_CMODE,
-                                           SILC_STATUS_ERR_NO_SUCH_CHANNEL);
+                                           SILC_STATUS_ERR_NO_SUCH_CHANNEL,
+                                           0);
       goto out;
     }
   }
 
   /* 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);
+                                         SILC_STATUS_ERR_NOT_ON_CHANNEL, 0);
     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)) {
-    silc_server_command_send_status_reply(cmd, SILC_COMMAND_CMODE,
-                                         SILC_STATUS_ERR_NO_CHANNEL_PRIV);
+  if (set_mask && !silc_server_check_cmode_rights(server, channel, chl, 
+                                                 mode_mask)) {
+    silc_server_command_send_status_reply(
+                            cmd, SILC_COMMAND_CMODE,
+                            (!(chl->mode & SILC_CHANNEL_UMODE_CHANOP) ? 
+                             SILC_STATUS_ERR_NO_CHANNEL_PRIV :
+                             SILC_STATUS_ERR_NO_CHANNEL_FOPRIV), 0);
+    goto out;
+  }
+
+  /* If mode mask was not sent as argument then merely return the current
+     mode mask to the sender. */
+  if (!set_mask) {
+    unsigned char m[4];
+    SILC_PUT32_MSB(channel->mode, m);
+    packet = silc_command_reply_payload_encode_va(SILC_COMMAND_CMODE,
+                                                 SILC_STATUS_OK, 0, ident, 2,
+                                                 2, tmp_id, tmp_len2,
+                                                 3, m, sizeof(m));
+    silc_server_packet_send(server, cmd->sock, SILC_PACKET_COMMAND_REPLY, 0,
+                           packet->data, packet->len, FALSE);
+    silc_buffer_free(packet);
     goto out;
   }
 
@@ -3774,32 +3982,32 @@ 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);
     if (!tmp) {
       if (!(channel->mode & SILC_CHANNEL_MODE_ULIMIT)) {
        silc_server_command_send_status_reply(cmd, SILC_COMMAND_CMODE,
-                                  SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
+                                  SILC_STATUS_ERR_NOT_ENOUGH_PARAMS, 0);
        goto out;
       }
     } else {
@@ -3820,47 +4028,54 @@ SILC_SERVER_CMD_FUNC(cmode)
       tmp = silc_argument_get_arg_type(cmd->args, 4, NULL);
       if (!tmp) {
        silc_server_command_send_status_reply(cmd, SILC_COMMAND_CMODE,
-                                  SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
+                                  SILC_STATUS_ERR_NOT_ENOUGH_PARAMS, 0);
        goto out;
       }
 
       /* Save the passphrase */
-      channel->passphrase = strdup(tmp);
+      passphrase = channel->passphrase = silc_memdup(tmp, strlen(tmp));
     }
   } else {
     if (channel->mode & SILC_CHANNEL_MODE_PASSPHRASE) {
       /* Passphrase mode is unset. remove the passphrase */
-      if (channel->passphrase) {
-       silc_free(channel->passphrase);
-       channel->passphrase = NULL;
-      }
+      silc_free(channel->passphrase);
+      channel->passphrase = NULL;
     }
   }
 
   if (mode_mask & SILC_CHANNEL_MODE_CIPHER) {
     if (!(channel->mode & SILC_CHANNEL_MODE_CIPHER)) {
       /* Cipher to use protect the traffic */
+      SilcCipher newkey, oldkey;
 
       /* Get cipher */
       cipher = silc_argument_get_arg_type(cmd->args, 5, NULL);
       if (!cipher) {
        silc_server_command_send_status_reply(cmd, SILC_COMMAND_CMODE,
-                                  SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
+                                  SILC_STATUS_ERR_NOT_ENOUGH_PARAMS, 0);
        goto out;
       }
 
       /* Delete old cipher and allocate the new one */
-      silc_cipher_free(channel->channel_key);
-      if (!silc_cipher_alloc(cipher, &channel->channel_key)) {
+      if (!silc_cipher_alloc(cipher, &newkey)) {
        silc_server_command_send_status_reply(cmd, SILC_COMMAND_CMODE,
-                                      SILC_STATUS_ERR_UNKNOWN_ALGORITHM);
+                                      SILC_STATUS_ERR_UNKNOWN_ALGORITHM, 0);
        goto out;
       }
 
+      oldkey = channel->channel_key;
+      channel->channel_key = newkey;
+
       /* Re-generate channel key */
-      if (!silc_server_create_channel_key(server, channel, 0))
+      if (!silc_server_create_channel_key(server, channel, 0)) {
+       /* We don't have new key, revert to old one */
+       channel->channel_key = oldkey;
        goto out;
-    
+      }
+
+      /* Remove old channel key for good */
+      silc_cipher_free(oldkey);
+
       /* Send the channel key. This sends it to our local clients and if
         we are normal server to our router as well. */
       silc_server_send_channel_key(server, NULL, channel, 
@@ -3871,21 +4086,29 @@ SILC_SERVER_CMD_FUNC(cmode)
     if (channel->mode & SILC_CHANNEL_MODE_CIPHER) {
       /* Cipher mode is unset. Remove the cipher and revert back to 
         default cipher */
+      SilcCipher newkey, oldkey;
       cipher = channel->cipher;
 
       /* Delete old cipher and allocate default one */
-      silc_cipher_free(channel->channel_key);
-      if (!silc_cipher_alloc(cipher ? cipher : SILC_DEFAULT_CIPHER, 
-                            &channel->channel_key)) {
+      if (!silc_cipher_alloc(cipher ? cipher : SILC_DEFAULT_CIPHER, &newkey)) {
        silc_server_command_send_status_reply(cmd, SILC_COMMAND_CMODE,
-                                  SILC_STATUS_ERR_UNKNOWN_ALGORITHM);
+                                  SILC_STATUS_ERR_UNKNOWN_ALGORITHM, 0);
        goto out;
       }
 
+      oldkey = channel->channel_key;
+      channel->channel_key = newkey;
+
       /* Re-generate channel key */
-      if (!silc_server_create_channel_key(server, channel, 0))
+      if (!silc_server_create_channel_key(server, channel, 0)) {
+       /* We don't have new key, revert to old one */
+       channel->channel_key = oldkey;
        goto out;
+      }
       
+      /* Remove old channel key for good */
+      silc_cipher_free(oldkey);
+
       /* Send the channel key. This sends it to our local clients and if
         we are normal server to our router as well. */
       silc_server_send_channel_key(server, NULL, channel, 
@@ -3898,28 +4121,30 @@ SILC_SERVER_CMD_FUNC(cmode)
     if (!(channel->mode & SILC_CHANNEL_MODE_HMAC)) {
       /* HMAC to use protect the traffic */
       unsigned char hash[32];
+      SilcHmac newhmac;
 
       /* Get hmac */
       hmac = silc_argument_get_arg_type(cmd->args, 6, NULL);
       if (!hmac) {
        silc_server_command_send_status_reply(cmd, SILC_COMMAND_CMODE,
-                                  SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
+                                  SILC_STATUS_ERR_NOT_ENOUGH_PARAMS, 0);
        goto out;
       }
 
       /* Delete old hmac and allocate the new one */
-      silc_hmac_free(channel->hmac);
-      if (!silc_hmac_alloc(hmac, NULL, &channel->hmac)) {
+      if (!silc_hmac_alloc(hmac, NULL, &newhmac)) {
        silc_server_command_send_status_reply(cmd, SILC_COMMAND_CMODE,
-                                      SILC_STATUS_ERR_UNKNOWN_ALGORITHM);
+                                      SILC_STATUS_ERR_UNKNOWN_ALGORITHM, 0);
        goto out;
       }
 
+      silc_hmac_free(channel->hmac);
+      channel->hmac = newhmac;
+
       /* Set the HMAC key out of current channel key. The client must do
         this locally. */
       silc_hash_make(silc_hmac_get_hash(channel->hmac), channel->key, 
-                    channel->key_len / 8, 
-                    hash);
+                    channel->key_len / 8, hash);
       silc_hmac_set_key(channel->hmac, hash, 
                        silc_hash_len(silc_hmac_get_hash(channel->hmac)));
       memset(hash, 0, sizeof(hash));
@@ -3928,18 +4153,21 @@ SILC_SERVER_CMD_FUNC(cmode)
     if (channel->mode & SILC_CHANNEL_MODE_HMAC) {
       /* Hmac mode is unset. Remove the hmac and revert back to 
         default hmac */
+      SilcHmac newhmac;
       unsigned char hash[32];
       hmac = channel->hmac_name;
 
       /* Delete old hmac and allocate default one */
       silc_hmac_free(channel->hmac);
-      if (!silc_hmac_alloc(hmac ? hmac : SILC_DEFAULT_HMAC, NULL, 
-                          &channel->hmac)) {
+      if (!silc_hmac_alloc(hmac ? hmac : SILC_DEFAULT_HMAC, NULL, &newhmac)) {
        silc_server_command_send_status_reply(cmd, SILC_COMMAND_CMODE,
-                                      SILC_STATUS_ERR_UNKNOWN_ALGORITHM);
+                                      SILC_STATUS_ERR_UNKNOWN_ALGORITHM, 0);
        goto out;
       }
 
+      silc_hmac_free(channel->hmac);
+      channel->hmac = newhmac;
+
       /* Set the HMAC key out of current channel key. The client must do
         this locally. */
       silc_hash_make(silc_hmac_get_hash(channel->hmac), channel->key, 
@@ -3955,47 +4183,28 @@ SILC_SERVER_CMD_FUNC(cmode)
     if (chl->mode & SILC_CHANNEL_UMODE_CHANFO) {
       if (!(channel->mode & SILC_CHANNEL_MODE_FOUNDER_AUTH)) {
        /* Set the founder authentication */
-       SilcAuthPayload auth;
-       
        tmp = silc_argument_get_arg_type(cmd->args, 7, &tmp_len);
        if (!tmp) {
-         silc_server_command_send_status_reply(cmd, SILC_COMMAND_CMODE,
-                                    SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
+         silc_server_command_send_status_reply(
+                                    cmd, SILC_COMMAND_CMODE,
+                                    SILC_STATUS_ERR_NOT_ENOUGH_PARAMS, 0);
          goto out;
        }
 
-       auth = silc_auth_payload_parse(tmp, tmp_len);
-       if (!auth) {
+       /* Verify the payload before setting the mode */
+       if (!silc_auth_verify_data(tmp, tmp_len, SILC_AUTH_PUBLIC_KEY, 
+                                  idata->public_key, 0, idata->hash,
+                                  client->id, SILC_ID_CLIENT)) {
          silc_server_command_send_status_reply(cmd, SILC_COMMAND_CMODE,
-                                    SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
+                                               SILC_STATUS_ERR_AUTH_FAILED,
+                                               0);
          goto out;
        }
 
        /* Save the public key */
-       tmp = silc_pkcs_public_key_encode(idata->public_key, &tmp_len);
-       silc_pkcs_public_key_decode(tmp, tmp_len, &channel->founder_key);
-       silc_free(tmp);
-       
-       channel->founder_method = silc_auth_get_method(auth);
-
-       if (channel->founder_method == SILC_AUTH_PASSWORD) {
-         tmp = silc_auth_get_data(auth, &tmp_len);
-         channel->founder_passwd = 
-           silc_calloc(tmp_len + 1, sizeof(*channel->founder_passwd));
-         memcpy(channel->founder_passwd, tmp, tmp_len);
-         channel->founder_passwd_len = tmp_len;
-       } else {
-         /* Verify the payload before setting the mode */
-         if (!silc_auth_verify(auth, channel->founder_method, 
-                               channel->founder_key, 0, idata->hash,
-                               client->id, SILC_ID_CLIENT)) {
-           silc_server_command_send_status_reply(cmd, SILC_COMMAND_CMODE,
-                                                 SILC_STATUS_ERR_AUTH_FAILED);
-           goto out;
-         }
-       }
-
-       silc_auth_payload_free(auth);
+       channel->founder_key = silc_pkcs_public_key_copy(idata->public_key);
+       founder_key = channel->founder_key;
+       fkey = silc_pkcs_public_key_encode(founder_key, &fkey_len);
       }
     }
   } else {
@@ -4003,10 +4212,7 @@ SILC_SERVER_CMD_FUNC(cmode)
       if (channel->mode & SILC_CHANNEL_MODE_FOUNDER_AUTH) {
        if (channel->founder_key)
          silc_pkcs_public_key_free(channel->founder_key);
-       if (channel->founder_passwd) {
-         silc_free(channel->founder_passwd);
-         channel->founder_passwd = NULL;
-       }
+       channel->founder_key = NULL;
       }
     }
   }
@@ -4014,14 +4220,17 @@ SILC_SERVER_CMD_FUNC(cmode)
   /* Finally, set the mode */
   channel->mode = mode_mask;
 
-  /* Send CMODE_CHANGE notify */
+  /* Send CMODE_CHANGE notify. */
   cidp = silc_id_payload_encode(client->id, SILC_ID_CLIENT);
   silc_server_send_notify_to_channel(server, NULL, channel, FALSE,
-                                    SILC_NOTIFY_TYPE_CMODE_CHANGE, 4,
+                                    SILC_NOTIFY_TYPE_CMODE_CHANGE, 6,
                                     cidp->data, cidp->len, 
                                     tmp_mask, 4,
                                     cipher, cipher ? strlen(cipher) : 0,
-                                    hmac, hmac ? strlen(hmac) : 0);
+                                    hmac, hmac ? strlen(hmac) : 0,
+                                    passphrase, passphrase ? 
+                                    strlen(passphrase) : 0,
+                                    fkey, fkey_len);
 
   /* Set CMODE notify type to network */
   if (!server->standalone)
@@ -4029,21 +4238,22 @@ SILC_SERVER_CMD_FUNC(cmode)
                                  server->server_type == SILC_ROUTER ? 
                                  TRUE : FALSE, channel,
                                  mode_mask, client->id, SILC_ID_CLIENT,
-                                 cipher, hmac);
+                                 cipher, hmac, passphrase, founder_key);
 
   /* Send command reply to sender */
   packet = silc_command_reply_payload_encode_va(SILC_COMMAND_CMODE,
-                                               SILC_STATUS_OK, ident, 2,
+                                               SILC_STATUS_OK, 0, ident, 2,
                                                2, tmp_id, tmp_len2,
                                                3, tmp_mask, 4);
   silc_server_packet_send(server, cmd->sock, SILC_PACKET_COMMAND_REPLY, 0, 
                          packet->data, packet->len, FALSE);
     
   silc_buffer_free(packet);
-  silc_free(channel_id);
-  silc_free(cidp);
+  silc_buffer_free(cidp);
 
  out:
+  silc_free(fkey);
+  silc_free(channel_id);
   silc_server_command_free(cmd);
 }
 
@@ -4062,9 +4272,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);
 
@@ -4072,13 +4282,13 @@ SILC_SERVER_CMD_FUNC(cumode)
   tmp_ch_id = silc_argument_get_arg_type(cmd->args, 1, &tmp_ch_len);
   if (!tmp_ch_id) {
     silc_server_command_send_status_reply(cmd, SILC_COMMAND_CUMODE,
-                                         SILC_STATUS_ERR_NO_CHANNEL_ID);
+                                         SILC_STATUS_ERR_NO_CHANNEL_ID, 0);
     goto out;
   }
-  channel_id = silc_id_payload_parse_id(tmp_ch_id, tmp_ch_len);
+  channel_id = silc_id_payload_parse_id(tmp_ch_id, tmp_ch_len, NULL);
   if (!channel_id) {
     silc_server_command_send_status_reply(cmd, SILC_COMMAND_CUMODE,
-                                         SILC_STATUS_ERR_NO_CHANNEL_ID);
+                                         SILC_STATUS_ERR_NO_CHANNEL_ID, 0);
     goto out;
   }
 
@@ -4090,27 +4300,26 @@ SILC_SERVER_CMD_FUNC(cumode)
                                             channel_id, NULL);
     if (!channel) {
       silc_server_command_send_status_reply(cmd, SILC_COMMAND_CUMODE,
-                                           SILC_STATUS_ERR_NO_SUCH_CHANNEL);
+                                           SILC_STATUS_ERR_NO_SUCH_CHANNEL,
+                                           0);
       goto out;
     }
   }
 
   /* 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);
+                                         SILC_STATUS_ERR_NOT_ON_CHANNEL, 0);
     goto out;
   }
-
-  /* Check that client has rights to change other's rights */
-  silc_hash_table_find(channel->user_list, client, NULL, (void *)&chl);
   sender_mask = chl->mode;
   
   /* Get the target client's channel mode mask */
   tmp_mask = silc_argument_get_arg_type(cmd->args, 2, NULL);
   if (!tmp_mask) {
     silc_server_command_send_status_reply(cmd, SILC_COMMAND_CUMODE,
-                                         SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
+                                         SILC_STATUS_ERR_NOT_ENOUGH_PARAMS,
+                                         0);
     goto out;
   }
   SILC_GET32_MSB(target_mask, tmp_mask);
@@ -4119,13 +4328,13 @@ SILC_SERVER_CMD_FUNC(cumode)
   tmp_id = silc_argument_get_arg_type(cmd->args, 3, &tmp_len);
   if (!tmp_id) {
     silc_server_command_send_status_reply(cmd, SILC_COMMAND_CUMODE,
-                                         SILC_STATUS_ERR_NO_CLIENT_ID);
+                                         SILC_STATUS_ERR_NO_CLIENT_ID, 0);
     goto out;
   }
-  client_id = silc_id_payload_parse_id(tmp_id, tmp_len);
+  client_id = silc_id_payload_parse_id(tmp_id, tmp_len, NULL);
   if (!client_id) {
     silc_server_command_send_status_reply(cmd, SILC_COMMAND_CUMODE,
-                                         SILC_STATUS_ERR_NO_CLIENT_ID);
+                                         SILC_STATUS_ERR_NO_CLIENT_ID, 0);
     goto out;
   }
 
@@ -4141,21 +4350,17 @@ SILC_SERVER_CMD_FUNC(cumode)
       !(sender_mask & SILC_CHANNEL_UMODE_CHANFO) &&
       !(sender_mask & SILC_CHANNEL_UMODE_CHANOP)) {
     silc_server_command_send_status_reply(cmd, SILC_COMMAND_CUMODE,
-                                         SILC_STATUS_ERR_NO_CHANNEL_PRIV);
+                                         SILC_STATUS_ERR_NO_CHANNEL_PRIV, 0);
     goto out;
   }
 
   /* 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);
+                                SILC_STATUS_ERR_USER_NOT_ON_CHANNEL, 0);
       goto out;
     }
-
-    /* Get entry to the channel user list */
-    silc_hash_table_find(channel->user_list, target_client, NULL, 
-                        (void *)&chl);
   }
 
   /* 
@@ -4164,52 +4369,50 @@ 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,
+                                         0);
     goto out;
   }
 
   if (target_mask & SILC_CHANNEL_UMODE_CHANFO) {
+    if (target_client != client) {
+      silc_server_command_send_status_reply(cmd, SILC_COMMAND_CUMODE,
+                                           SILC_STATUS_ERR_NOT_YOU, 0);
+      goto out;
+    }
+
     if (!(chl->mode & SILC_CHANNEL_UMODE_CHANFO)) {
       /* The client tries to claim the founder rights. */
       unsigned char *tmp_auth;
-      uint32 tmp_auth_len, auth_len;
-      void *auth;
-      
-      if (target_client != client) {
-       silc_server_command_send_status_reply(cmd, SILC_COMMAND_CUMODE,
-                                             SILC_STATUS_ERR_NOT_YOU);
-       goto out;
-      }
+      SilcUInt32 tmp_auth_len;
 
       if (!(channel->mode & SILC_CHANNEL_MODE_FOUNDER_AUTH) ||
-         !channel->founder_key) {
+         !channel->founder_key || !idata->public_key ||
+         !silc_pkcs_public_key_compare(channel->founder_key, 
+                                       idata->public_key)) {
        silc_server_command_send_status_reply(cmd, SILC_COMMAND_CUMODE,
-                                             SILC_STATUS_ERR_NOT_YOU);
+                                             SILC_STATUS_ERR_AUTH_FAILED, 0);
        goto out;
       }
 
       tmp_auth = silc_argument_get_arg_type(cmd->args, 4, &tmp_auth_len);
       if (!tmp_auth) {
        silc_server_command_send_status_reply(cmd, SILC_COMMAND_CUMODE,
-                                    SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
+                                             SILC_STATUS_ERR_AUTH_FAILED, 0);
        goto out;
       }
-      
-      auth = (channel->founder_method == SILC_AUTH_PASSWORD ?
-             (void *)channel->founder_passwd : (void *)channel->founder_key);
-      auth_len = (channel->founder_method == SILC_AUTH_PASSWORD ?
-                 channel->founder_passwd_len : 0);
-      
-      if (!silc_auth_verify_data(tmp_auth, tmp_auth_len,
-                                channel->founder_method, auth, auth_len,
-                                idata->hash, client->id, SILC_ID_CLIENT)) {
+
+      /* Verify the authentication payload */
+      if (!silc_auth_verify_data(tmp_auth, tmp_auth_len, SILC_AUTH_PUBLIC_KEY,
+                                channel->founder_key, 0, idata->hash,
+                                client->id, SILC_ID_CLIENT)) {
        silc_server_command_send_status_reply(cmd, SILC_COMMAND_CUMODE,
-                                             SILC_STATUS_ERR_AUTH_FAILED);
+                                             SILC_STATUS_ERR_AUTH_FAILED, 0);
        goto out;
       }
-      
+
       sender_mask = chl->mode |= SILC_CHANNEL_UMODE_CHANFO;
       notify = TRUE;
     }
@@ -4221,7 +4424,7 @@ SILC_SERVER_CMD_FUNC(cumode)
        notify = TRUE;
       } else {
        silc_server_command_send_status_reply(cmd, SILC_COMMAND_CUMODE,
-                                             SILC_STATUS_ERR_NOT_YOU);
+                                             SILC_STATUS_ERR_NOT_YOU, 0);
        goto out;
       }
     }
@@ -4233,7 +4436,8 @@ SILC_SERVER_CMD_FUNC(cumode)
       if (!(sender_mask & SILC_CHANNEL_UMODE_CHANOP) &&
          !(sender_mask & SILC_CHANNEL_UMODE_CHANFO)) {
        silc_server_command_send_status_reply(cmd, SILC_COMMAND_CUMODE,
-                                             SILC_STATUS_ERR_NO_CHANNEL_PRIV);
+                                             SILC_STATUS_ERR_NO_CHANNEL_PRIV,
+                                             0);
        goto out;
       }
 
@@ -4245,7 +4449,8 @@ SILC_SERVER_CMD_FUNC(cumode)
       if (!(sender_mask & SILC_CHANNEL_UMODE_CHANOP) &&
          !(sender_mask & SILC_CHANNEL_UMODE_CHANFO)) {
        silc_server_command_send_status_reply(cmd, SILC_COMMAND_CUMODE,
-                                             SILC_STATUS_ERR_NO_CHANNEL_PRIV);
+                                             SILC_STATUS_ERR_NO_CHANNEL_PRIV,
+                                             0);
        goto out;
       }
 
@@ -4255,12 +4460,84 @@ SILC_SERVER_CMD_FUNC(cumode)
     }
   }
 
+  if (target_mask & SILC_CHANNEL_UMODE_BLOCK_MESSAGES) {
+    if (target_client != client) {
+      silc_server_command_send_status_reply(cmd, SILC_COMMAND_CUMODE,
+                                           SILC_STATUS_ERR_NOT_YOU, 0);
+      goto out;
+    }
+
+    if (!(chl->mode & SILC_CHANNEL_UMODE_BLOCK_MESSAGES)) {
+      chl->mode |= SILC_CHANNEL_UMODE_BLOCK_MESSAGES;
+      notify = TRUE;
+    }
+  } else {
+    if (chl->mode & SILC_CHANNEL_UMODE_BLOCK_MESSAGES) {
+      if (target_client != client) {
+       silc_server_command_send_status_reply(cmd, SILC_COMMAND_CUMODE,
+                                             SILC_STATUS_ERR_NOT_YOU, 0);
+       goto out;
+      }
+
+      chl->mode &= ~SILC_CHANNEL_UMODE_BLOCK_MESSAGES;
+      notify = TRUE;
+    }
+  }
+
+  if (target_mask & SILC_CHANNEL_UMODE_BLOCK_MESSAGES_USERS) {
+    if (target_client != client) {
+      silc_server_command_send_status_reply(cmd, SILC_COMMAND_CUMODE,
+                                           SILC_STATUS_ERR_NOT_YOU, 0);
+      goto out;
+    }
+
+    if (!(chl->mode & SILC_CHANNEL_UMODE_BLOCK_MESSAGES_USERS)) {
+      chl->mode |= SILC_CHANNEL_UMODE_BLOCK_MESSAGES_USERS;
+      notify = TRUE;
+    }
+  } else {
+    if (chl->mode & SILC_CHANNEL_UMODE_BLOCK_MESSAGES_USERS) {
+      if (target_client != client) {
+       silc_server_command_send_status_reply(cmd, SILC_COMMAND_CUMODE,
+                                             SILC_STATUS_ERR_NOT_YOU, 0);
+       goto out;
+      }
+
+      chl->mode &= ~SILC_CHANNEL_UMODE_BLOCK_MESSAGES_USERS;
+      notify = TRUE;
+    }
+  }
+
+  if (target_mask & SILC_CHANNEL_UMODE_BLOCK_MESSAGES_ROBOTS) {
+    if (target_client != client) {
+      silc_server_command_send_status_reply(cmd, SILC_COMMAND_CUMODE,
+                                           SILC_STATUS_ERR_NOT_YOU, 0);
+      goto out;
+    }
+
+    if (!(chl->mode & SILC_CHANNEL_UMODE_BLOCK_MESSAGES_ROBOTS)) {
+      chl->mode |= SILC_CHANNEL_UMODE_BLOCK_MESSAGES_ROBOTS;
+      notify = TRUE;
+    }
+  } else {
+    if (chl->mode & SILC_CHANNEL_UMODE_BLOCK_MESSAGES_ROBOTS) {
+      if (target_client != client) {
+       silc_server_command_send_status_reply(cmd, SILC_COMMAND_CUMODE,
+                                             SILC_STATUS_ERR_NOT_YOU, 0);
+       goto out;
+      }
+
+      chl->mode &= ~SILC_CHANNEL_UMODE_BLOCK_MESSAGES_ROBOTS;
+      notify = TRUE;
+    }
+  }
+
   idp = silc_id_payload_encode(client->id, SILC_ID_CLIENT);
   tmp_id = silc_argument_get_arg_type(cmd->args, 3, &tmp_len);
 
   /* Send notify to channel, notify only if mode was actually changed. */
   if (notify) {
-    silc_server_send_notify_to_channel(server, NULL, channel, FALSE,
+    silc_server_send_notify_to_channel(server, NULL, channel, FALSE, 
                                       SILC_NOTIFY_TYPE_CUMODE_CHANGE, 3,
                                       idp->data, idp->len,
                                       tmp_mask, 4, 
@@ -4278,7 +4555,7 @@ SILC_SERVER_CMD_FUNC(cumode)
 
   /* Send command reply to sender */
   packet = silc_command_reply_payload_encode_va(SILC_COMMAND_CUMODE,
-                                               SILC_STATUS_OK, ident, 3,
+                                               SILC_STATUS_OK, 0, ident, 3,
                                                2, tmp_mask, 4,
                                                3, tmp_ch_id, tmp_ch_len,
                                                4, tmp_id, tmp_len);
@@ -4307,8 +4584,8 @@ SILC_SERVER_CMD_FUNC(kick)
   SilcChannelEntry channel;
   SilcChannelClientEntry chl;
   SilcBuffer idp;
-  uint32 tmp_len;
-  unsigned char *tmp, *comment;
+  SilcUInt32 tmp_len, target_idp_len;
+  unsigned char *tmp, *comment, *target_idp;
 
   SILC_SERVER_COMMAND_CHECK(SILC_COMMAND_LEAVE, cmd, 1, 3);
 
@@ -4316,13 +4593,13 @@ SILC_SERVER_CMD_FUNC(kick)
   tmp = silc_argument_get_arg_type(cmd->args, 1, &tmp_len);
   if (!tmp) {
     silc_server_command_send_status_reply(cmd, SILC_COMMAND_KICK,
-                                         SILC_STATUS_ERR_NO_CHANNEL_ID);
+                                         SILC_STATUS_ERR_NO_CHANNEL_ID, 0);
     goto out;
   }
-  channel_id = silc_id_payload_parse_id(tmp, tmp_len);
+  channel_id = silc_id_payload_parse_id(tmp, tmp_len, NULL);
   if (!channel_id) {
     silc_server_command_send_status_reply(cmd, SILC_COMMAND_KICK,
-                                         SILC_STATUS_ERR_NO_CHANNEL_ID);
+                                         SILC_STATUS_ERR_NO_CHANNEL_ID, 0);
     goto out;
   }
 
@@ -4334,37 +4611,38 @@ SILC_SERVER_CMD_FUNC(kick)
                                             channel_id, NULL);
     if (!channel) {
       silc_server_command_send_status_reply(cmd, SILC_COMMAND_KICK,
-                                           SILC_STATUS_ERR_NO_SUCH_CHANNEL);
+                                           SILC_STATUS_ERR_NO_SUCH_CHANNEL,
+                                           0);
       goto out;
     }
   }
 
   /* 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);
+                                         SILC_STATUS_ERR_NOT_ON_CHANNEL, 0);
     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) {
+  if (!(chl->mode & SILC_CHANNEL_UMODE_CHANOP) &&
+      !(chl->mode & SILC_CHANNEL_UMODE_CHANFO)) {
     silc_server_command_send_status_reply(cmd, SILC_COMMAND_KICK,
-                                         SILC_STATUS_ERR_NO_CHANNEL_PRIV);
+                                         SILC_STATUS_ERR_NO_CHANNEL_PRIV, 0);
     goto out;
   }
   
   /* Get target Client ID */
-  tmp = silc_argument_get_arg_type(cmd->args, 2, &tmp_len);
-  if (!tmp) {
+  target_idp = silc_argument_get_arg_type(cmd->args, 2, &target_idp_len);
+  if (!target_idp) {
     silc_server_command_send_status_reply(cmd, SILC_COMMAND_KICK,
-                                         SILC_STATUS_ERR_NO_CLIENT_ID);
+                                         SILC_STATUS_ERR_NO_CLIENT_ID, 0);
     goto out;
   }
-  client_id = silc_id_payload_parse_id(tmp, tmp_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);
+                                         SILC_STATUS_ERR_NO_CLIENT_ID, 0);
     goto out;
   }
 
@@ -4376,22 +4654,23 @@ 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,
+                                         0);
+    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);
+                                         SILC_STATUS_ERR_NO_CHANNEL_FOPRIV,
+                                         0);
     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);
@@ -4400,15 +4679,15 @@ SILC_SERVER_CMD_FUNC(kick)
 
   /* Send command reply to sender */
   silc_server_command_send_status_reply(cmd, SILC_COMMAND_KICK, 
-                                       SILC_STATUS_OK);
+                                       SILC_STATUS_OK, 0);
 
   /* Send KICKED notify to local clients on the channel */
-  idp = silc_id_payload_encode(target_client->id, SILC_ID_CLIENT);
+  idp = silc_id_payload_encode(client->id, SILC_ID_CLIENT);
   silc_server_send_notify_to_channel(server, NULL, channel, FALSE,
-                                    SILC_NOTIFY_TYPE_KICKED, 
-                                    comment ? 2 : 1,
-                                    idp->data, idp->len,
-                                    comment, comment ? strlen(comment) : 0);
+                                    SILC_NOTIFY_TYPE_KICKED, 3,
+                                    target_idp, target_idp_len,
+                                    comment, comment ? strlen(comment) : 0,
+                                    idp->data, idp->len);
   silc_buffer_free(idp);
 
   /* Remove the client from the channel. If the channel does not exist
@@ -4423,7 +4702,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 */
@@ -4450,9 +4729,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);
 
@@ -4463,19 +4744,21 @@ SILC_SERVER_CMD_FUNC(oper)
   username = silc_argument_get_arg_type(cmd->args, 1, &tmp_len);
   if (!username) {
     silc_server_command_send_status_reply(cmd, SILC_COMMAND_OPER,
-                                         SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
+                                         SILC_STATUS_ERR_NOT_ENOUGH_PARAMS,
+                                         0);
     goto out;
   }
 
   /* 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,
-                                           SILC_STATUS_ERR_AUTH_FAILED);
+                                           SILC_STATUS_ERR_AUTH_FAILED,
+                                           0);
       goto out;
     }
   }
@@ -4484,163 +4767,417 @@ SILC_SERVER_CMD_FUNC(oper)
   auth = silc_argument_get_arg_type(cmd->args, 2, &tmp_len);
   if (!auth) {
     silc_server_command_send_status_reply(cmd, SILC_COMMAND_OPER,
-                                         SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
+                                         SILC_STATUS_ERR_NOT_ENOUGH_PARAMS,
+                                         0);
     goto out;
   }
 
-  /* 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);
+                                         SILC_STATUS_ERR_AUTH_FAILED,
+                                         0);
     goto out;
   }
 
   /* Client is now server operator */
   client->mode |= SILC_UMODE_SERVER_OPERATOR;
 
+  /* Update statistics */
+  if (client->connection)
+    server->stat.my_server_ops++;
+  if (server->server_type == SILC_ROUTER)
+    server->stat.server_ops++;
+
   /* Send UMODE change to primary router */
   if (!server->standalone)
     silc_server_send_notify_umode(server, server->router->connection, TRUE,
                                  client->id, client->mode);
 
+  /* Check if anyone is watching this nickname */
+  if (server->server_type == SILC_ROUTER)
+    silc_server_check_watcher_list(server, client, NULL,
+                                  SILC_NOTIFY_TYPE_UMODE_CHANGE);
+
   /* Send reply to the sender */
   silc_server_command_send_status_reply(cmd, SILC_COMMAND_OPER,
-                                       SILC_STATUS_OK);
+                                       SILC_STATUS_OK, 0);
 
  out:
   silc_server_command_free(cmd);
 }
 
-/* Server side of SILCOPER command. Client uses this comand to obtain router
-   operator privileges to this router. */
+SILC_TASK_CALLBACK(silc_server_command_detach_cb)
+{
+  QuitInternal q = (QuitInternal)context;
+  SilcClientEntry client = (SilcClientEntry)q->sock->user_data;
 
-SILC_SERVER_CMD_FUNC(silcoper)
+  /* If there is pending outgoing data for the client then purge it
+     to the network before closing connection. */
+  silc_server_packet_queue_purge(q->server, q->sock);
+
+  /* Close the connection on our side */
+  client->router = NULL;
+  client->connection = NULL;
+  q->sock->user_data = NULL;
+  silc_server_close_connection(q->server, q->sock);
+
+  silc_free(q);
+}
+
+SILC_TASK_CALLBACK(silc_server_command_detach_timeout)
+{
+  QuitInternal q = (QuitInternal)context;
+  SilcClientEntry client = (SilcClientEntry)q->sock;
+
+  SILC_LOG_DEBUG(("Start"));
+
+  if (client->mode & SILC_UMODE_DETACHED)
+    silc_server_free_client_data(q->server, NULL, client, TRUE,
+                                "Detach timeout");
+  silc_free(q);
+}
+
+/* Server side of DETACH command.  Detached the client from the network
+   by closing the connection but preserving the session. */
+
+SILC_SERVER_CMD_FUNC(detach)
 {
   SilcServerCommandContext cmd = (SilcServerCommandContext)context;
   SilcServer server = cmd->server;
   SilcClientEntry client = (SilcClientEntry)cmd->sock->user_data;
-  unsigned char *username, *auth;
-  uint32 tmp_len;
-  SilcServerConfigSectionAdminConnection *admin;
-  SilcIDListData idata = (SilcIDListData)client;
+  QuitInternal q;
 
-  SILC_SERVER_COMMAND_CHECK(SILC_COMMAND_SILCOPER, cmd, 1, 2);
+  if (server->config->detach_disabled) {
+    silc_server_command_send_status_reply(cmd, SILC_COMMAND_DETACH,
+                                         SILC_STATUS_ERR_UNKNOWN_COMMAND,
+                                         0);
+    goto out;
+  }
 
-  if (!client || cmd->sock->type != SILC_SOCKET_TYPE_CLIENT)
+  if (cmd->sock->type != SILC_SOCKET_TYPE_CLIENT)
     goto out;
 
-  if (server->server_type != SILC_ROUTER) {
-    silc_server_command_send_status_reply(cmd, SILC_COMMAND_SILCOPER,
-                                         SILC_STATUS_ERR_AUTH_FAILED);
-    goto out;
+  SILC_SERVER_COMMAND_CHECK(SILC_COMMAND_DETACH, cmd, 0, 0);
+
+  /* Send the user mode notify to notify that client is detached */
+  client->mode |= SILC_UMODE_DETACHED;
+  client->data.status &= ~SILC_IDLIST_STATUS_RESUMED;
+  client->last_command = 0;
+  client->fast_command = 0;
+  if (!server->standalone)
+    silc_server_send_notify_umode(server, server->router->connection,
+                                 server->server_type == SILC_SERVER ?
+                                 FALSE : TRUE, client->id, client->mode);
+
+  /* Check if anyone is watching this nickname */
+  if (server->server_type == SILC_ROUTER)
+    silc_server_check_watcher_list(server, client, NULL,
+                                  SILC_NOTIFY_TYPE_UMODE_CHANGE);
+
+  q = silc_calloc(1, sizeof(*q));
+  q->server = server;
+  q->sock = cmd->sock;
+  silc_schedule_task_add(server->schedule, 0, silc_server_command_detach_cb,
+                        q, 0, 200000, SILC_TASK_TIMEOUT, SILC_TASK_PRI_LOW);
+
+  if (server->config->detach_timeout) {
+    q = silc_calloc(1, sizeof(*q));
+    q->server = server;
+    q->sock = (void *)client;
+    silc_schedule_task_add(server->schedule, 0, 
+                          silc_server_command_detach_timeout,
+                          q, server->config->detach_timeout * 60,
+                          0, SILC_TASK_TIMEOUT, SILC_TASK_PRI_LOW);
   }
 
-  /* Get the username */
-  username = silc_argument_get_arg_type(cmd->args, 1, &tmp_len);
-  if (!username) {
-    silc_server_command_send_status_reply(cmd, SILC_COMMAND_SILCOPER,
-                                         SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
+  /* Send reply to the sender */
+  silc_server_command_send_status_reply(cmd, SILC_COMMAND_DETACH,
+                                       SILC_STATUS_OK, 0);
+
+ out:
+  silc_server_command_free(cmd);
+}
+
+/* Server side of WATCH command. */
+
+SILC_SERVER_CMD_FUNC(watch)
+{
+  SilcServerCommandContext cmd = (SilcServerCommandContext)context;
+  SilcServer server = cmd->server;
+  char *add_nick, *del_nick;
+  SilcUInt32 add_nick_len, del_nick_len, tmp_len;
+  char nick[128 + 1];
+  unsigned char hash[16], *tmp;
+  SilcClientEntry client;
+  SilcClientID *client_id = NULL;
+
+  SILC_SERVER_COMMAND_CHECK(SILC_COMMAND_WATCH, cmd, 1, 3);
+
+  if (server->server_type == SILC_SERVER && !server->standalone) {
+    if (!cmd->pending) {
+      /* Send the command to router */
+      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);
+
+      silc_server_packet_send(server, server->router->connection,
+                             SILC_PACKET_COMMAND, cmd->packet->flags,
+                             tmpbuf->data, tmpbuf->len, TRUE);
+
+      /* Reprocess this packet after received reply from router */
+      silc_server_command_pending(server, SILC_COMMAND_WATCH,
+                                 silc_command_get_ident(cmd->payload),
+                                 silc_server_command_watch,
+                                 silc_server_command_dup(cmd));
+      cmd->pending = TRUE;
+      silc_command_set_ident(cmd->payload, old_ident);
+      silc_buffer_free(tmpbuf);
+    } else if (context2) {
+      /* Received reply from router, just send same data to the client. */
+      SilcServerCommandReplyContext reply = context2;
+      SilcStatus status;
+      silc_command_get_status(reply->payload, &status, NULL);
+      silc_server_command_send_status_reply(cmd, SILC_COMMAND_WATCH, status,
+                                           0);
+    }
+
     goto out;
   }
 
-  /* Get the admin configuration */
-  admin = silc_server_config_find_admin(server->config, cmd->sock->ip,
-                                       username, client->nickname);
-  if (!admin) {
-    admin = silc_server_config_find_admin(server->config, cmd->sock->hostname,
-                                         username, client->nickname);
-    if (!admin) {
-      silc_server_command_send_status_reply(cmd, SILC_COMMAND_SILCOPER,
-                                           SILC_STATUS_ERR_AUTH_FAILED);
-      goto out;
-    }
+  /* We are router and keep the watch list for local cell */
+
+  /* Get the client ID */
+  tmp = silc_argument_get_arg_type(cmd->args, 1, &tmp_len);
+  if (!tmp) {
+    silc_server_command_send_status_reply(cmd, SILC_COMMAND_WATCH,
+                                         SILC_STATUS_ERR_NOT_ENOUGH_PARAMS,
+                                         0);
+    goto out;
+  }
+  client_id = silc_id_payload_parse_id(tmp, tmp_len, NULL);
+  if (!client_id) {
+    silc_server_command_send_status_reply(cmd, SILC_COMMAND_WATCH,
+                                         SILC_STATUS_ERR_NO_SUCH_CLIENT_ID,
+                                         0);
+    goto out;
   }
 
-  /* Get the authentication payload */
-  auth = silc_argument_get_arg_type(cmd->args, 2, &tmp_len);
-  if (!auth) {
-    silc_server_command_send_status_reply(cmd, SILC_COMMAND_SILCOPER,
-                                         SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
+  /* Get the client entry which must be in local list */
+  client = silc_idlist_find_client_by_id(server->local_list, 
+                                        client_id, TRUE, NULL);
+  if (!client) {
+    silc_server_command_send_status_reply(cmd, SILC_COMMAND_WATCH,
+                                         SILC_STATUS_ERR_NO_SUCH_CLIENT_ID,
+                                         0);
     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,
-                                         SILC_STATUS_ERR_AUTH_FAILED);
+  /* Take nickname */
+  add_nick = silc_argument_get_arg_type(cmd->args, 2, &add_nick_len);
+  del_nick = silc_argument_get_arg_type(cmd->args, 3, &del_nick_len);
+  if (!add_nick && !del_nick) {
+    silc_server_command_send_status_reply(cmd, SILC_COMMAND_WATCH,
+                                         SILC_STATUS_ERR_NOT_ENOUGH_PARAMS,
+                                         0);
     goto out;
   }
 
-  /* Client is now router operator */
-  client->mode |= SILC_UMODE_ROUTER_OPERATOR;
+  if (add_nick && add_nick_len > 128)
+    add_nick[128] = '\0';
+  if (del_nick && del_nick_len > 128)
+    del_nick[128] = '\0';
 
-  /* Send UMODE change to primary router */
-  if (!server->standalone)
-    silc_server_send_notify_umode(server, server->router->connection, TRUE,
-                                 client->id, client->mode);
+  memset(nick, 0, sizeof(nick));
 
-  /* Send reply to the sender */
-  silc_server_command_send_status_reply(cmd, SILC_COMMAND_SILCOPER,
-                                       SILC_STATUS_OK);
+  /* Add new nickname to be watched in our cell */
+  if (add_nick) {
+    if (silc_server_name_bad_chars(add_nick, strlen(add_nick)) == TRUE) {
+      silc_server_command_send_status_reply(cmd, SILC_COMMAND_WATCH,
+                                           SILC_STATUS_ERR_BAD_NICKNAME, 0);
+      goto out;
+    }
+
+    /* Hash the nick, we have the hash saved, not nicks because we can
+       do one to one mapping to the nick from Client ID hash this way. */
+    silc_to_lower(add_nick, nick, sizeof(nick) - 1);
+    silc_hash_make(server->md5hash, nick, strlen(nick), hash);
+
+    /* Check whether this client is already watching this nickname */
+    if (silc_hash_table_find_by_context(server->watcher_list, hash, 
+                                       client, NULL)) {
+      /* Nickname is alredy being watched for this client */
+      silc_server_command_send_status_reply(cmd, SILC_COMMAND_WATCH,
+                                           SILC_STATUS_ERR_NICKNAME_IN_USE,
+                                           0);
+      goto out;
+    }
+
+    /* Get the nickname from the watcher list and use the same key in
+       new entries as well.  If key doesn't exist then create it. */
+    if (!silc_hash_table_find(server->watcher_list, hash, (void **)&tmp, NULL))
+      tmp = silc_memdup(hash, CLIENTID_HASH_LEN);
+
+    /* Add the client to the watcher list with the specified nickname hash. */
+    silc_hash_table_add(server->watcher_list, tmp, client);
+  }
+
+  /* Delete nickname from watch list */
+  if (del_nick) {
+    if (silc_server_name_bad_chars(del_nick, strlen(del_nick)) == TRUE) {
+      silc_server_command_send_status_reply(cmd, SILC_COMMAND_WATCH,
+                                           SILC_STATUS_ERR_BAD_NICKNAME, 0);
+      goto out;
+    }
+
+    /* Hash the nick, we have the hash saved, not nicks because we can
+       do one to one mapping to the nick from Client ID hash this way. */
+    silc_to_lower(del_nick, nick, sizeof(nick) - 1);
+    silc_hash_make(server->md5hash, nick, strlen(nick), hash);
+
+    /* Check that this client is watching for this nickname */
+    if (!silc_hash_table_find_by_context(server->watcher_list, hash, 
+                                        client, (void **)&tmp)) {
+      /* Nickname is alredy being watched for this client */
+      silc_server_command_send_status_reply(cmd, SILC_COMMAND_WATCH,
+                                           SILC_STATUS_ERR_NO_SUCH_NICK, 0);
+      goto out;
+    }
+
+    /* Delete the nickname from the watcher list. */
+    silc_hash_table_del_by_context(server->watcher_list, hash, client);
+
+    /* Now check whether there still exists entries with this key, if not
+       then free the key to not leak memory. */
+    if (!silc_hash_table_find(server->watcher_list, hash, NULL, NULL))
+      silc_free(tmp);
+  }
+
+  silc_server_command_send_status_reply(cmd, SILC_COMMAND_WATCH,
+                                       SILC_STATUS_OK, 0);
 
  out:
+  silc_free(client_id);
   silc_server_command_free(cmd);
 }
 
-/* Server side command of CONNECT. Connects us to the specified remote
-   server or router. */
+/* Server side of SILCOPER command. Client uses this comand to obtain router
+   operator privileges to this router. */
 
-SILC_SERVER_CMD_FUNC(connect)
+SILC_SERVER_CMD_FUNC(silcoper)
 {
   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;
+  unsigned char *username, *auth;
+  SilcUInt32 tmp_len;
+  SilcServerConfigAdmin *admin;
+  SilcIDListData idata = (SilcIDListData)client;
+  bool result = FALSE;
+  SilcPublicKey cached_key;
 
-  SILC_SERVER_COMMAND_CHECK(SILC_COMMAND_CONNECT, cmd, 1, 2);
+  SILC_SERVER_COMMAND_CHECK(SILC_COMMAND_SILCOPER, 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);
+  if (server->server_type != SILC_ROUTER) {
+    silc_server_command_send_status_reply(cmd, SILC_COMMAND_SILCOPER,
+                                         SILC_STATUS_ERR_AUTH_FAILED, 0);
     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);
+  /* Get the username */
+  username = silc_argument_get_arg_type(cmd->args, 1, &tmp_len);
+  if (!username) {
+    silc_server_command_send_status_reply(cmd, SILC_COMMAND_SILCOPER,
+                                         SILC_STATUS_ERR_NOT_ENOUGH_PARAMS,
+                                         0);
     goto out;
   }
 
-  /* Get the remote server */
-  host = silc_argument_get_arg_type(cmd->args, 1, &tmp_len);
-  if (!host) {
-    silc_server_command_send_status_reply(cmd, SILC_COMMAND_CONNECT,
-                                         SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
+  /* Get the admin configuration */
+  admin = silc_server_config_find_admin(server, cmd->sock->ip,
+                                       username, client->nickname);
+  if (!admin) {
+    admin = silc_server_config_find_admin(server, cmd->sock->hostname,
+                                         username, client->nickname);
+    if (!admin) {
+      silc_server_command_send_status_reply(cmd, SILC_COMMAND_SILCOPER,
+                                           SILC_STATUS_ERR_AUTH_FAILED, 0);
+      goto out;
+    }
+  }
+
+  /* Get the authentication payload */
+  auth = silc_argument_get_arg_type(cmd->args, 2, &tmp_len);
+  if (!auth) {
+    silc_server_command_send_status_reply(cmd, SILC_COMMAND_SILCOPER,
+                                         SILC_STATUS_ERR_NOT_ENOUGH_PARAMS,
+                                         0);
     goto out;
   }
 
-  /* Get port */
-  tmp = silc_argument_get_arg_type(cmd->args, 2, &tmp_len);
-  if (tmp)
-    SILC_GET32_MSB(port, tmp);
+  /* 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, 0);
+    goto out;
+  }
 
-  /* Create the connection. It is done with timeout and is async. */
-  silc_server_create_connection(server, host, port);
+  /* Client is now router operator */
+  client->mode |= SILC_UMODE_ROUTER_OPERATOR;
+
+  /* Update statistics */
+  if (client->connection)
+    server->stat.my_router_ops++;
+  if (server->server_type == SILC_ROUTER)
+    server->stat.router_ops++;
+
+  /* Send UMODE change to primary router */
+  if (!server->standalone)
+    silc_server_send_notify_umode(server, server->router->connection, TRUE,
+                                 client->id, client->mode);
+
+  /* Check if anyone is watching this nickname */
+  if (server->server_type == SILC_ROUTER)
+    silc_server_check_watcher_list(server, client, NULL,
+                                  SILC_NOTIFY_TYPE_UMODE_CHANGE);
 
   /* Send reply to the sender */
-  silc_server_command_send_status_reply(cmd, SILC_COMMAND_CONNECT,
-                                       SILC_STATUS_OK);
+  silc_server_command_send_status_reply(cmd, SILC_COMMAND_SILCOPER,
+                                       SILC_STATUS_OK, 0);
 
  out:
   silc_server_command_free(cmd);
@@ -4659,8 +5196,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;
@@ -4670,10 +5207,10 @@ 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);
+                                           SILC_STATUS_ERR_NO_CHANNEL_ID, 0);
       goto out;
     }
   }
@@ -4687,25 +5224,23 @@ SILC_SERVER_CMD_FUNC(ban)
                                             channel_id, NULL);
     if (!channel) {
       silc_server_command_send_status_reply(cmd, SILC_COMMAND_BAN,
-                                           SILC_STATUS_ERR_NO_SUCH_CHANNEL);
+                                           SILC_STATUS_ERR_NO_SUCH_CHANNEL,
+                                           0);
       goto out;
     }
   }
 
   /* 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);
+                                         SILC_STATUS_ERR_NOT_ON_CHANNEL, 0);
     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,
-                                         SILC_STATUS_ERR_NO_CHANNEL_PRIV);
+                                         SILC_STATUS_ERR_NO_CHANNEL_PRIV, 0);
     goto out;
   }
 
@@ -4755,19 +5290,13 @@ SILC_SERVER_CMD_FUNC(ban)
                                TRUE : FALSE, channel, add, del);
 
   /* Send the reply back to the client */
-  if (channel->ban_list)
-    packet = 
-      silc_command_reply_payload_encode_va(SILC_COMMAND_BAN,
-                                          SILC_STATUS_OK, ident, 2,
-                                          2, id, id_len,
-                                          3, channel->ban_list, 
-                                          strlen(channel->ban_list) - 1);
-  else
-    packet = 
-      silc_command_reply_payload_encode_va(SILC_COMMAND_BAN,
-                                          SILC_STATUS_OK, ident, 1,
-                                          2, id, id_len);
-
+  packet = 
+    silc_command_reply_payload_encode_va(SILC_COMMAND_BAN,
+                                        SILC_STATUS_OK, 0, ident, 2,
+                                        2, id, id_len,
+                                        3, channel->ban_list, 
+                                        channel->ban_list ? 
+                                        strlen(channel->ban_list) -1 : 0);
   silc_server_packet_send(server, cmd->sock, SILC_PACKET_COMMAND_REPLY, 0, 
                          packet->data, packet->len, FALSE);
     
@@ -4778,111 +5307,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)
@@ -4893,7 +5317,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);
@@ -4902,13 +5326,13 @@ SILC_SERVER_CMD_FUNC(leave)
   tmp = silc_argument_get_arg_type(cmd->args, 1, &len);
   if (!tmp) {
     silc_server_command_send_status_reply(cmd, SILC_COMMAND_LEAVE,
-                                         SILC_STATUS_ERR_NO_CHANNEL_ID);
+                                         SILC_STATUS_ERR_NO_CHANNEL_ID, 0);
     goto out;
   }
-  id = silc_id_payload_parse_id(tmp, len);
+  id = silc_id_payload_parse_id(tmp, len, NULL);
   if (!id) {
     silc_server_command_send_status_reply(cmd, SILC_COMMAND_LEAVE,
-                                         SILC_STATUS_ERR_NO_CHANNEL_ID);
+                                         SILC_STATUS_ERR_NO_CHANNEL_ID, 0);
     goto out;
   }
 
@@ -4918,15 +5342,16 @@ SILC_SERVER_CMD_FUNC(leave)
     channel = silc_idlist_find_channel_by_id(server->global_list, id, NULL);
     if (!channel) {
       silc_server_command_send_status_reply(cmd, SILC_COMMAND_LEAVE,
-                                           SILC_STATUS_ERR_NO_SUCH_CHANNEL);
+                                           SILC_STATUS_ERR_NO_SUCH_CHANNEL,
+                                           0);
       goto out;
     }
   }
 
   /* 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);
+                                         SILC_STATUS_ERR_NOT_ON_CHANNEL, 0);
     goto out;
   }
 
@@ -4937,8 +5362,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, 0, 2, tmp, len);
 
   /* Remove client from channel */
   if (!silc_server_remove_from_one_channel(server, sock, channel, id_entry,
@@ -4974,12 +5399,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);
@@ -4992,15 +5417,15 @@ SILC_SERVER_CMD_FUNC(users)
 
   if (!channel_id && !channel_name) {
     silc_server_command_send_status_reply(cmd, SILC_COMMAND_USERS,
-                                         SILC_STATUS_ERR_NO_CHANNEL_ID);
+                                         SILC_STATUS_ERR_NO_CHANNEL_ID, 0);
     goto out;
   }
 
   if (channel_id) {
-    id = silc_id_payload_parse_id(channel_id, channel_id_len);
+    id = silc_id_payload_parse_id(channel_id, channel_id_len, NULL);
     if (!id) {
       silc_server_command_send_status_reply(cmd, SILC_COMMAND_USERS,
-                                           SILC_STATUS_ERR_NO_CHANNEL_ID);
+                                           SILC_STATUS_ERR_NO_CHANNEL_ID, 0);
       goto out;
     }
   }
@@ -5014,12 +5439,12 @@ SILC_SERVER_CMD_FUNC(users)
     channel = silc_idlist_find_channel_by_name(server->local_list, 
                                               channel_name, NULL);
 
-  if (!channel) {
+  if (!channel || channel->disabled || !channel->users_resolved) {
     if (server->server_type != SILC_ROUTER && !server->standalone &&
        !cmd->pending) {
       SilcBuffer tmpbuf;
       
-      silc_command_set_ident(cmd->payload, silc_rng_get_rn16(server->rng));
+      silc_command_set_ident(cmd->payload, ++server->cmd_ident);
       tmpbuf = silc_command_payload_encode_payload(cmd->payload);
       
       /* Send USERS command */
@@ -5030,15 +5455,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. */
@@ -5050,25 +5473,20 @@ SILC_SERVER_CMD_FUNC(users)
     if (!channel) {
       /* Channel really does not exist */
       silc_server_command_send_status_reply(cmd, SILC_COMMAND_USERS,
-                                           SILC_STATUS_ERR_NO_SUCH_CHANNEL);
+                                           SILC_STATUS_ERR_NO_SUCH_CHANNEL,
+                                           0);
       goto out;
     }
   }
 
   /* If the channel is private or secret do not send anything, unless the
-     user requesting this command is on the channel. */
+     user requesting this command is on the channel or is server */
   if (cmd->sock->type == SILC_SOCKET_TYPE_CLIENT) {
     if (channel->mode & (SILC_CHANNEL_MODE_PRIVATE | SILC_CHANNEL_MODE_SECRET)
-       && !silc_server_client_on_channel(cmd->sock->user_data, channel)) {
-      silc_server_command_send_status_reply(cmd, SILC_COMMAND_USERS,
-                                           SILC_STATUS_ERR_NO_SUCH_CHANNEL);
-      goto out;
-    }
-  } else {
-    if (channel->mode & 
-       (SILC_CHANNEL_MODE_PRIVATE | SILC_CHANNEL_MODE_SECRET)) {
+       && !silc_server_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);
+                                           SILC_STATUS_ERR_NOT_ON_CHANNEL, 0);
       goto out;
     }
   }
@@ -5083,7 +5501,7 @@ SILC_SERVER_CMD_FUNC(users)
   /* Send reply */
   idp = silc_id_payload_encode(channel->id, SILC_ID_CHANNEL);
   packet = silc_command_reply_payload_encode_va(SILC_COMMAND_USERS,
-                                               SILC_STATUS_OK, ident, 4,
+                                               SILC_STATUS_OK, 0, ident, 4,
                                                2, idp->data, idp->len,
                                                3, lc, 4,
                                                4, client_id_list->data,
@@ -5116,24 +5534,27 @@ 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"));
 
   tmp = silc_argument_get_arg_type(cmd->args, 1, &tmp_len);
   if (!tmp) {
     silc_server_command_send_status_reply(cmd, SILC_COMMAND_GETKEY,
-                                         SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
+                                         SILC_STATUS_ERR_NOT_ENOUGH_PARAMS,
+                                         0);
     goto out;
   }
-  idp = silc_id_payload_parse_data(tmp, tmp_len);
+  idp = silc_id_payload_parse(tmp, tmp_len);
   if (!idp) {
     silc_server_command_send_status_reply(cmd, SILC_COMMAND_GETKEY,
-                                         SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
+                                         SILC_STATUS_ERR_NOT_ENOUGH_PARAMS,
+                                         0);
     goto out;
   }
 
@@ -5150,19 +5571,20 @@ SILC_SERVER_CMD_FUNC(getkey)
                                             client_id, TRUE, NULL);
     
     if ((!client && !cmd->pending && !server->standalone) ||
-       (client && !client->connection && !cmd->pending) ||
+       (client && !client->connection && !cmd->pending &&
+        !(client->mode & SILC_UMODE_DETACHED)) ||
        (client && !client->data.public_key && !cmd->pending)) {
       SilcBuffer tmpbuf;
-      uint16 old_ident;
+      SilcUInt16 old_ident;
       SilcSocketConnection dest_sock;
       
       dest_sock = silc_server_get_client_route(server, NULL, 0, 
-                                              client_id, NULL);
+                                              client_id, NULL, NULL);
       if (!dest_sock)
        goto out;
       
       old_ident = silc_command_get_ident(cmd->payload);
-      silc_command_set_ident(cmd->payload, silc_rng_get_rn16(server->rng));
+      silc_command_set_ident(cmd->payload, ++server->cmd_ident);
       tmpbuf = silc_command_payload_encode_payload(cmd->payload);
       
       silc_server_packet_send(server, dest_sock,
@@ -5172,30 +5594,30 @@ 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) {
       silc_server_command_send_status_reply(cmd, SILC_COMMAND_GETKEY,
-                                           SILC_STATUS_ERR_NO_SUCH_CLIENT_ID);
+                                           SILC_STATUS_ERR_NO_SUCH_CLIENT_ID,
+                                           0);
       goto out;
     }
 
     /* 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,
@@ -5225,10 +5647,10 @@ 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, silc_rng_get_rn16(server->rng));
+      silc_command_set_ident(cmd->payload, ++server->cmd_ident);
       tmpbuf = silc_command_payload_encode_payload(cmd->payload);
       
       silc_server_packet_send(server, server->router->connection,
@@ -5238,29 +5660,30 @@ 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) {
       silc_server_command_send_status_reply(cmd, SILC_COMMAND_GETKEY,
-                                           SILC_STATUS_ERR_NO_SUCH_SERVER_ID);
+                                           SILC_STATUS_ERR_NO_SUCH_SERVER_ID,
+                                           0);
       goto out;
     }
 
     /* 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,
@@ -5278,7 +5701,7 @@ SILC_SERVER_CMD_FUNC(getkey)
 
   tmp = silc_argument_get_arg_type(cmd->args, 1, &tmp_len);
   packet = silc_command_reply_payload_encode_va(SILC_COMMAND_GETKEY,
-                                               SILC_STATUS_OK, ident, 
+                                               SILC_STATUS_OK, 0, ident, 
                                                pkdata ? 2 : 1,
                                                2, tmp, tmp_len,
                                                3, pkdata, pklen);
@@ -5296,3 +5719,173 @@ 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_SERVER_OPERATOR) &&
+      !(client->mode & SILC_UMODE_ROUTER_OPERATOR)) {
+    silc_server_command_send_status_reply(cmd, SILC_COMMAND_PRIV_CONNECT,
+                                         SILC_STATUS_ERR_NO_SERVER_PRIV, 0);
+    goto out;
+  }
+
+  if (server->server_type == SILC_ROUTER && 
+      client->mode & SILC_UMODE_SERVER_OPERATOR) {
+    silc_server_command_send_status_reply(cmd, SILC_COMMAND_PRIV_CONNECT,
+                                         SILC_STATUS_ERR_NO_ROUTER_PRIV, 0);
+    goto out;
+  }
+
+  /* Get the remote server */
+  host = silc_argument_get_arg_type(cmd->args, 1, &tmp_len);
+  if (!host) {
+    silc_server_command_send_status_reply(cmd, SILC_COMMAND_PRIV_CONNECT,
+                                         SILC_STATUS_ERR_NOT_ENOUGH_PARAMS,
+                                         0);
+    goto out;
+  }
+
+  /* Get port */
+  tmp = silc_argument_get_arg_type(cmd->args, 2, &tmp_len);
+  if (tmp)
+    SILC_GET32_MSB(port, tmp);
+
+  /* Create the connection. It is done with timeout and is async. */
+  silc_server_create_connection(server, host, port);
+
+  /* Send reply to the sender */
+  silc_server_command_send_status_reply(cmd, SILC_COMMAND_PRIV_CONNECT,
+                                       SILC_STATUS_OK, 0);
+
+ out:
+  silc_server_command_free(cmd);
+}
+
+/* Server side command of CLOSE. Closes connection to a specified server. */
+SILC_SERVER_CMD_FUNC(close)
+{
+  SilcServerCommandContext cmd = (SilcServerCommandContext)context;
+  SilcServer server = cmd->server;
+  SilcClientEntry client = (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_SERVER_OPERATOR) &&
+      !(client->mode & SILC_UMODE_ROUTER_OPERATOR)) {
+    silc_server_command_send_status_reply(cmd, SILC_COMMAND_PRIV_CLOSE,
+                                         SILC_STATUS_ERR_NO_SERVER_PRIV,
+                                         0);
+    goto out;
+  }
+
+  /* Get the remote server */
+  name = silc_argument_get_arg_type(cmd->args, 1, &tmp_len);
+  if (!name) {
+    silc_server_command_send_status_reply(cmd, SILC_COMMAND_PRIV_CLOSE,
+                                         SILC_STATUS_ERR_NOT_ENOUGH_PARAMS,
+                                         0);
+    goto out;
+  }
+
+  /* Get port */
+  tmp = silc_argument_get_arg_type(cmd->args, 2, &tmp_len);
+  if (tmp)
+    SILC_GET32_MSB(port, tmp);
+
+  server_entry = silc_idlist_find_server_by_conn(server->local_list,
+                                                name, port, 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, 0);
+    goto out;
+  }
+
+  /* Send reply to the sender */
+  silc_server_command_send_status_reply(cmd, SILC_COMMAND_PRIV_CLOSE,
+                                       SILC_STATUS_OK, 0);
+
+  /* Close the connection to the server */
+  sock = (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_SERVER_OPERATOR) &&
+      !(client->mode & SILC_UMODE_ROUTER_OPERATOR)) {
+    silc_server_command_send_status_reply(cmd, SILC_COMMAND_PRIV_SHUTDOWN,
+                                         SILC_STATUS_ERR_NO_SERVER_PRIV,
+                                         0);
+    goto out;
+  }
+
+  /* Send reply to the sender */
+  silc_server_command_send_status_reply(cmd, SILC_COMMAND_PRIV_SHUTDOWN,
+                                       SILC_STATUS_OK, 0);
+
+  /* Then, gracefully, or not, bring the server down. */
+  silc_server_stop(server);
+  exit(0);
+
+ out:
+  silc_server_command_free(cmd);
+}