Fixed topic annoucning, server signoff handling, added -D option,
[silc.git] / apps / silcd / command.c
index e83ef754ccd1655f2302e6f8fc571d9ce1be12fa..116de4fd794d18ce7951a64804917502ac396d01 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,
+                                    SilcStatus status,
+                                    SilcStatus error,
+                                    SilcUInt32 arg_type,
                                     const unsigned char *arg,
-                                    uint32 arg_len);
+                                    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);
 }
@@ -173,6 +187,8 @@ void silc_server_command_process(SilcServer server,
   SilcServerCommand *cmd;
   SilcCommand command;
 
+  SILC_LOG_DEBUG(("Start"));
+
   /* Allocate command context. This must be free'd by the
      command routine receiving it. */
   ctx = silc_server_command_alloc();
@@ -185,7 +201,6 @@ void silc_server_command_process(SilcServer server,
                                            packet->buffer->len);
   if (!ctx->payload) {
     SILC_LOG_ERROR(("Bad command payload, packet dropped"));
-    silc_buffer_free(packet->buffer);
     silc_packet_context_free(packet);
     silc_socket_free(ctx->sock);
     silc_free(ctx);
@@ -199,9 +214,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;
   }
@@ -222,26 +237,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;
   }
 
@@ -251,6 +265,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 */
@@ -303,7 +319,7 @@ silc_server_command_dup(SilcServerCommandContext ctx)
 
 bool silc_server_command_pending(SilcServer server,
                                 SilcCommand reply_cmd,
-                                uint16 ident,
+                                SilcUInt16 ident,
                                 SilcCommandCb callback,
                                 void *context)
 {
@@ -333,7 +349,7 @@ bool silc_server_command_pending(SilcServer server,
 
 void silc_server_command_pending_del(SilcServer server,
                                     SilcCommand reply_cmd,
-                                    uint16 ident)
+                                    SilcUInt16 ident)
 {
   SilcServerCommandPending *r;
 
@@ -353,8 +369,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;
@@ -381,14 +397,15 @@ silc_server_command_pending_check(SilcServer server,
 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,
@@ -403,17 +420,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,
+                                    SilcStatus status,
+                                    SilcStatus error,
+                                    SilcUInt32 arg_type,
                                     const unsigned char *arg,
-                                    uint32 arg_len)
+                                    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,
@@ -432,16 +450,10 @@ 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) {
+  if (!silc_command_get_status(cmdr->payload, NULL, NULL)) {
     SilcBuffer buffer;
 
     /* Send the same command reply payload */
@@ -464,62 +476,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);
       }
     }
   }
@@ -527,7 +547,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;
 
@@ -537,25 +557,27 @@ 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)
@@ -633,7 +655,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);
 
@@ -684,53 +706,48 @@ silc_server_command_whois_check(SilcServerCommandContext cmd,
 static void
 silc_server_command_whois_send_reply(SilcServerCommandContext cmd,
                                     SilcClientEntry *clients,
-                                    uint32 clients_count,
-                                    int count,
-                                    const char *nickname,
+                                    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, valid_count;
-  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;
 
-  /* Process only valid clients and ignore those that are not registered. */
-  valid_count = 0;
-  for (i = 0; i < clients_count; i++) {
-    if (clients[i]->data.status & SILC_IDLIST_STATUS_REGISTERED)
-      valid_count++;
-    else
-      clients[i] = NULL;
-  }
+  if (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 (!valid_count) {
-    /* No valid clients found, send error reply */
-    if (nickname) {
+    if (!valid_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, nickname, strlen(nickname));
-    } else if (client_ids && client_ids[0]) {
-      SilcBuffer idp = silc_id_payload_encode(client_ids[0], SILC_ID_CLIENT);
-      silc_server_command_send_status_data(cmd, SILC_COMMAND_WHOIS,
-                                          SILC_STATUS_ERR_NO_SUCH_CLIENT_ID,
-                                          2, idp->data, idp->len);
-      silc_buffer_free(idp);
+      return;
     }
-    return;
   }
 
   /* Start processing found clients. */
+  status = SILC_STATUS_OK;
   if (valid_count > 1)
     status = SILC_STATUS_LIST_START;
-  else
-    status = SILC_STATUS_OK;
 
   for (i = 0, k = 0; i < clients_count; i++) {
     entry = clients[i];
@@ -739,7 +756,7 @@ silc_server_command_whois_send_reply(SilcServerCommandContext cmd,
 
     if (k >= 1)
       status = SILC_STATUS_LIST_ITEM;
-    if (valid_count > 1 && k == valid_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;
@@ -766,14 +783,19 @@ 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;
@@ -781,14 +803,12 @@ silc_server_command_whois_send_reply(SilcServerCommandContext cmd,
       fingerprint = NULL;
       
     SILC_PUT32_MSB(entry->mode, mode);
-
-    if (entry->connection) {
+    if (entry->connection)
       SILC_PUT32_MSB((time(NULL) - entry->data.last_receive), idle);
-    }
 
     packet = 
       silc_command_reply_payload_encode_va(SILC_COMMAND_WHOIS,
-                                          status, ident, 8
+                                          status, 0, ident, 9
                                           2, idp->data, idp->len,
                                           3, nh, strlen(nh),
                                           4, uh, strlen(uh),
@@ -799,7 +819,10 @@ silc_server_command_whois_send_reply(SilcServerCommandContext cmd,
                                           7, mode, 4,
                                           8, idle, 4,
                                           9, fingerprint,
-                                          fingerprint ? 20 : 0);
+                                          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);
@@ -808,7 +831,49 @@ 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++;
   }
 }
@@ -818,7 +883,7 @@ silc_server_command_whois_send_router(SilcServerCommandContext cmd)
 {
   SilcServer server = cmd->server;
   SilcBuffer tmpbuf;
-  uint16 old_ident;
+  SilcUInt16 old_ident;
 
   old_ident = silc_command_get_ident(cmd->payload);
   silc_command_set_ident(cmd->payload, ++server->cmd_ident);
@@ -848,14 +913,15 @@ 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;
 
   /* Parse the whois request */
   if (!silc_server_command_whois_parse(cmd, &client_id, &client_id_count, 
                                       &nick, &server_name, &count,
-                                      SILC_COMMAND_WHOIS))
+                                      &error_client, &error_client_count))
     return 0;
 
   /* Send the WHOIS request to the router only if it included nickname.
@@ -898,9 +964,12 @@ silc_server_command_whois_process(SilcServerCommandContext 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,
@@ -918,7 +987,7 @@ 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 &&
@@ -930,17 +999,14 @@ silc_server_command_whois_process(SilcServerCommandContext cmd)
     }
 
     /* 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;
   }
 
@@ -956,6 +1022,7 @@ silc_server_command_whois_process(SilcServerCommandContext cmd)
 
   /* Send the command reply */
   silc_server_command_whois_send_reply(cmd, clients, clients_count,
+                                      error_client, error_client_count,
                                       count, nick, client_id);
 
  out:
@@ -965,6 +1032,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);
 
@@ -998,12 +1066,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;
   }
 
@@ -1013,7 +1082,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;
 
@@ -1023,7 +1092,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;
@@ -1034,7 +1103,7 @@ silc_server_command_whowas_check(SilcServerCommandContext cmd,
 
     if (!entry->nickname || !entry->username) {
       SilcBuffer tmpbuf;
-      uint16 old_ident;
+      SilcUInt16 old_ident;
 
       if (!entry->router)
        continue;
@@ -1054,7 +1123,6 @@ silc_server_command_whowas_check(SilcServerCommandContext cmd,
                                  silc_server_command_whowas, 
                                  silc_server_command_dup(cmd));
       cmd->pending = TRUE;
-      
       silc_command_set_ident(cmd->payload, old_ident);
 
       silc_buffer_free(tmpbuf);
@@ -1068,39 +1136,51 @@ 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 || !entry->id)
-      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;
 
     /* Send WHOWAS reply */
@@ -1130,7 +1210,7 @@ silc_server_command_whowas_send_reply(SilcServerCommandContext cmd,
       
     packet = 
       silc_command_reply_payload_encode_va(SILC_COMMAND_WHOWAS,
-                                          status, ident, 4, 
+                                          status, 0, ident, 4, 
                                           2, idp->data, idp->len,
                                           3, nh, strlen(nh),
                                           4, uh, strlen(uh),
@@ -1142,13 +1222,11 @@ silc_server_command_whowas_send_reply(SilcServerCommandContext cmd,
     
     silc_buffer_free(packet);
     silc_buffer_free(idp);
-  }
 
-  if (found == FALSE && entry)
-    silc_server_command_send_status_data(cmd, SILC_COMMAND_WHOWAS,
-                                        SILC_STATUS_ERR_NO_SUCH_NICK,
-                                        3, entry->nickname, 
-                                        strlen(entry->nickname));
+    if (status == SILC_STATUS_LIST_END)
+      break;
+    k++;
+  }
 }
 
 static int
@@ -1158,7 +1236,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;
 
@@ -1170,7 +1248,7 @@ silc_server_command_whowas_process(SilcServerCommandContext cmd)
       server->server_type == SILC_SERVER && !cmd->pending && 
       !server->standalone) {
     SilcBuffer tmpbuf;
-    uint16 old_ident;
+    SilcUInt16 old_ident;
 
     old_ident = silc_command_get_ident(cmd->payload);
     silc_command_set_ident(cmd->payload, ++server->cmd_ident);
@@ -1188,7 +1266,6 @@ silc_server_command_whowas_process(SilcServerCommandContext cmd)
                                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 +1306,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;
   }
@@ -1273,7 +1350,7 @@ silc_server_command_identify_send_router(SilcServerCommandContext cmd)
 {
   SilcServer server = cmd->server;
   SilcBuffer tmpbuf;
-  uint16 old_ident;
+  SilcUInt16 old_ident;
 
   old_ident = silc_command_get_ident(cmd->payload);
   silc_command_set_ident(cmd->payload, ++server->cmd_ident);
@@ -1298,22 +1375,23 @@ silc_server_command_identify_send_router(SilcServerCommandContext cmd)
 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;
@@ -1363,7 +1441,7 @@ silc_server_command_identify_parse(SilcServerCommandContext cmd,
       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 0;
       }
@@ -1387,7 +1465,7 @@ silc_server_command_identify_parse(SilcServerCommandContext cmd,
        /* 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));
+                                            0, 3, tmp, strlen(tmp));
        return 0;
       }
     }
@@ -1410,14 +1488,15 @@ silc_server_command_identify_parse(SilcServerCommandContext cmd,
        /* 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));
+                                            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);
+                                           SILC_STATUS_ERR_NOT_ENOUGH_PARAMS,
+                                           0);
       return 0;
     }
   } else {
@@ -1433,26 +1512,19 @@ silc_server_command_identify_parse(SilcServerCommandContext cmd,
        continue;
       
       idp = silc_id_payload_parse(tmp, len);
-      if (!idp) {
-       silc_free(*clients);
-       silc_free(*servers);
-       silc_free(*channels);
-       silc_server_command_send_status_reply(
-                                      cmd, SILC_COMMAND_IDENTIFY,
-                                      SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
-       return 0;
-      }
+      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));
@@ -1467,24 +1539,22 @@ silc_server_command_identify_parse(SilcServerCommandContext cmd,
            silc_free(*clients);
            silc_free(*servers);
            silc_free(*channels);
+           silc_free(*error_id);
            return -1;
-         } else {
-           silc_server_command_send_status_data(
-                                       cmd, SILC_COMMAND_IDENTIFY,
-                                       SILC_STATUS_ERR_NO_SUCH_CLIENT_ID,
-                                       2, tmp, len);
-           error = TRUE;
          }
+
+         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));
@@ -1499,23 +1569,20 @@ silc_server_command_identify_parse(SilcServerCommandContext cmd,
            silc_free(*clients);
            silc_free(*servers);
            silc_free(*channels);
+           silc_free(*error_id);
            return -1;
-         } else {
-           silc_server_command_send_status_data(
-                                        cmd, SILC_COMMAND_IDENTIFY,
-                                        SILC_STATUS_ERR_NO_SUCH_SERVER_ID,
-                                        2, tmp, len);
-           error = TRUE;
          }
+
+         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));
@@ -1530,14 +1597,12 @@ silc_server_command_identify_parse(SilcServerCommandContext cmd,
            silc_free(*clients);
            silc_free(*servers);
            silc_free(*channels);
+           silc_free(*error_id);
            return -1;
-         } else {
-           silc_server_command_send_status_data(
-                                        cmd, SILC_COMMAND_IDENTIFY,
-                                        SILC_STATUS_ERR_NO_SUCH_CHANNEL_ID,
-                                        2, tmp, len);
-           error = TRUE;
          }
+
+         ADD_ERROR((*error_id), (*error_id_count), NULL, 0, i + 5,
+                   SILC_STATUS_ERR_NO_SUCH_CHANNEL_ID);
        }
        break;
       }
@@ -1546,17 +1611,10 @@ 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;
 
@@ -1570,12 +1628,12 @@ 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;
 
@@ -1656,7 +1714,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);
 
@@ -1707,51 +1765,51 @@ 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, valid_count;
+  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;
+      }
 
-    /* Process only valid entries. */
-    valid_count = 0;
-    for (i = 0; i < clients_count; i++) {
-      if (clients[i]->data.status & SILC_IDLIST_STATUS_REGISTERED)
-       valid_count++;
-      else
-       clients[i] = NULL;
-    }
-
-    if (!valid_count) {
-      /* No valid entries found at all, just send error */
-      unsigned char *tmp;
-
-      tmp = silc_argument_get_arg_type(cmd->args, 1, NULL);
-      if (tmp) {
-       silc_server_command_send_status_data(cmd, SILC_COMMAND_IDENTIFY,
-                                            SILC_STATUS_ERR_NO_SUCH_NICK,
-                                            3, tmp, strlen(tmp));
-      } else {
-       tmp = silc_argument_get_arg_type(cmd->args, 5, (uint32 *)&len);
+      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_CLIENT_ID,
-                                            2, tmp, len);
+                                            SILC_STATUS_ERR_NO_SUCH_NICK, 0,
+                                            3, tmp, tmp ? strlen(tmp) : 0);
+       return;
       }
-      return;
     }
 
     /* Process all valid client entries and send command replies */
@@ -1767,12 +1825,10 @@ silc_server_command_identify_send_reply(SilcServerCommandContext cmd,
       if (k >= 1)
        status = SILC_STATUS_LIST_ITEM;
       if (valid_count > 1 && k == valid_count - 1 
-         && !servers_count && !channels_count)
+         && !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 */
 
@@ -1794,12 +1850,12 @@ silc_server_command_identify_send_reply(SilcServerCommandContext cmd,
 
       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);
@@ -1807,7 +1863,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));
@@ -1819,6 +1875,8 @@ silc_server_command_identify_send_reply(SilcServerCommandContext cmd,
       silc_buffer_free(packet);
       silc_buffer_free(idp);
       
+      if (status == SILC_STATUS_LIST_END)
+       break;
       k++;
     }
   }
@@ -1834,18 +1892,17 @@ 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);
       packet = 
        silc_command_reply_payload_encode_va(SILC_COMMAND_IDENTIFY,
-                                            status, ident, 2,
+                                            status, 0, ident, 2,
                                             2, idp->data, idp->len, 
                                             3, entry->server_name, 
                                             entry->server_name ? 
@@ -1856,6 +1913,8 @@ silc_server_command_identify_send_reply(SilcServerCommandContext cmd,
       silc_buffer_free(packet);
       silc_buffer_free(idp);
       
+      if (status == SILC_STATUS_LIST_END)
+       break;
       k++;
     }
   }
@@ -1871,18 +1930,16 @@ 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);
       packet = 
        silc_command_reply_payload_encode_va(SILC_COMMAND_IDENTIFY,
-                                            status, ident, 2,
+                                            status, 0, ident, 2,
                                             2, idp->data, idp->len, 
                                             3, entry->channel_name, 
                                             entry->channel_name ? 
@@ -1893,6 +1950,46 @@ silc_server_command_identify_send_reply(SilcServerCommandContext cmd,
       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++;
     }
   }
@@ -1901,27 +1998,29 @@ silc_server_command_identify_send_reply(SilcServerCommandContext cmd,
 static int
 silc_server_command_identify_process(SilcServerCommandContext cmd)
 {
-  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;
+  SilcUInt32 clients_count = 0, servers_count = 0, channels_count = 0;
+  SilcUInt32 errors_count = 0;
+  ResolveError errors = NULL;
 
   /* Parse the IDENTIFY request */
   ret = silc_server_command_identify_parse(cmd,
                                           &clients, &clients_count,
                                           &servers, &servers_count,
                                           &channels, &channels_count,
-                                          &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;
   }
@@ -1931,12 +2030,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;
 }
 
@@ -1951,25 +2052,6 @@ SILC_SERVER_CMD_FUNC(identify)
   silc_server_command_free(cmd);
 }
 
-/* Checks string for bad characters and returns TRUE if they are found. */
-
-static int silc_server_command_bad_chars(char *nick)
-{
-  int i;
-
-  for (i = 0; i < strlen(nick); i++) {
-    if (!isascii(nick[i]))
-      return TRUE;
-    if (nick[i] <= 32) return TRUE;
-    if (nick[i] == ' ') return TRUE;
-    if (nick[i] == '*') return TRUE;
-    if (nick[i] == '?') return TRUE;
-    if (nick[i] == ',') return TRUE;
-  }
-
-  return FALSE;
-}
-
 /* Server side of command NICK. Sets nickname for user. Setting
    nickname causes generation of a new client ID for the client. The
    new client ID is sent to the client after changing the nickname. */
@@ -1981,8 +2063,9 @@ SILC_SERVER_CMD_FUNC(nick)
   SilcServer server = cmd->server;
   SilcBuffer packet, nidp, oidp = NULL;
   SilcClientID *new_id;
+  SilcUInt32 nick_len;
   char *nick;
-  uint16 ident = silc_command_get_ident(cmd->payload);
+  SilcUInt16 ident = silc_command_get_ident(cmd->payload);
   int nickfail = 0;
 
   if (cmd->sock->type != SILC_SOCKET_TYPE_CLIENT)
@@ -1991,16 +2074,15 @@ SILC_SERVER_CMD_FUNC(nick)
   SILC_SERVER_COMMAND_CHECK(SILC_COMMAND_NICK, cmd, 1, 1);
 
   /* Check nickname */
-  nick = silc_argument_get_arg_type(cmd->args, 1, NULL);
-  if (silc_server_command_bad_chars(nick) == TRUE) {
+  nick = silc_argument_get_arg_type(cmd->args, 1, &nick_len);
+  if (nick_len > 128)
+    nick[128] = '\0';
+  if (silc_server_name_bad_chars(nick, nick_len) == TRUE) {
     silc_server_command_send_status_reply(cmd, SILC_COMMAND_NICK,
-                                         SILC_STATUS_ERR_BAD_NICKNAME);
+                                         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);
@@ -2013,6 +2095,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);
   }
 
@@ -2023,21 +2110,23 @@ 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, 
@@ -2047,15 +2136,23 @@ SILC_SERVER_CMD_FUNC(nick)
 
   /* 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);
 
@@ -2073,18 +2170,18 @@ 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, 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++) {
@@ -2129,7 +2226,7 @@ silc_server_command_list_send_reply(SilcServerCommandContext cmd,
     /* Send the reply */
     packet = 
       silc_command_reply_payload_encode_va(SILC_COMMAND_LIST, 
-                                          status, ident, 4,
+                                          status, 0, ident, 4,
                                           2, idp->data, idp->len,
                                           3, entry->channel_name, 
                                           strlen(entry->channel_name),
@@ -2161,14 +2258,14 @@ 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 */
     packet = 
       silc_command_reply_payload_encode_va(SILC_COMMAND_LIST, 
-                                          status, ident, 4,
+                                          status, 0, ident, 4,
                                           2, idp->data, idp->len,
                                           3, entry->channel_name, 
                                           strlen(entry->channel_name),
@@ -2192,9 +2289,9 @@ SILC_SERVER_CMD_FUNC(list)
   SilcServer server = cmd->server;
   SilcChannelID *channel_id = NULL;
   unsigned char *tmp;
-  uint32 tmp_len;
+  SilcUInt32 tmp_len;
   SilcChannelEntry *lchannels = NULL, *gchannels = NULL;
-  uint32 lch_count = 0, gch_count = 0;
+  SilcUInt32 lch_count = 0, gch_count = 0;
 
   SILC_SERVER_COMMAND_CHECK(SILC_COMMAND_LIST, cmd, 0, 1);
 
@@ -2203,7 +2300,7 @@ SILC_SERVER_CMD_FUNC(list)
   if (!cmd->pending && server->server_type == SILC_SERVER && 
       !server->standalone) {
     SilcBuffer tmpbuf;
-    uint16 old_ident;
+    SilcUInt16 old_ident;
     
     old_ident = silc_command_get_ident(cmd->payload);
     silc_command_set_ident(cmd->payload, ++server->cmd_ident);
@@ -2226,10 +2323,10 @@ SILC_SERVER_CMD_FUNC(list)
   /* 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;
     }
   }
@@ -2266,8 +2363,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);
 
@@ -2277,13 +2374,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;
   }
 
@@ -2295,7 +2392,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;
     }
   }
@@ -2305,30 +2403,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 */
@@ -2346,7 +2447,7 @@ SILC_SERVER_CMD_FUNC(topic)
     idp = silc_id_payload_encode(client->id, SILC_ID_CLIENT);
 
     /* Send notify about topic change to all clients on the channel */
-    silc_server_send_notify_to_channel(server, NULL, channel, TRUE, 
+    silc_server_send_notify_to_channel(server, NULL, channel, FALSE, 
                                       SILC_NOTIFY_TYPE_TOPIC_SET, 2,
                                       idp->data, idp->len,
                                       channel->topic, strlen(channel->topic));
@@ -2356,7 +2457,7 @@ SILC_SERVER_CMD_FUNC(topic)
   /* Send the topic to client as reply packet */
   idp = silc_id_payload_encode(channel_id, SILC_ID_CHANNEL);
   packet = silc_command_reply_payload_encode_va(SILC_COMMAND_TOPIC, 
-                                               SILC_STATUS_OK, ident, 2, 
+                                               SILC_STATUS_OK, 0, ident, 2, 
                                                2, idp->data, idp->len,
                                                3, channel->topic, 
                                                channel->topic ? 
@@ -2388,8 +2489,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);
 
@@ -2397,13 +2498,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;
   }
 
@@ -2415,48 +2516,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;
       }
       
@@ -2473,17 +2577,20 @@ SILC_SERVER_CMD_FUNC(invite)
     }
 
     /* 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;
     }
 
@@ -2508,18 +2615,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 */
@@ -2576,7 +2685,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 ?
@@ -2584,7 +2693,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);
@@ -2631,14 +2740,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;
@@ -2650,8 +2759,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);
@@ -2668,7 +2777,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);
 
@@ -2678,14 +2788,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;
   }
 
@@ -2693,25 +2803,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;
     }
   }
@@ -2719,53 +2834,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);
@@ -2781,9 +2863,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;
 
@@ -2795,10 +2877,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;
     }
   }
@@ -2812,7 +2894,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;
       }
     }
@@ -2832,10 +2915,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;
@@ -2854,7 +2937,7 @@ SILC_SERVER_CMD_FUNC(info)
        server->server_type != SILC_SERVER && entry && !entry->server_info) {
       /* Send to the server */
       SilcBuffer tmpbuf;
-      uint16 old_ident;
+      SilcUInt16 old_ident;
 
       old_ident = silc_command_get_ident(cmd->payload);
       silc_command_set_ident(cmd->payload, ++server->cmd_ident);
@@ -2878,7 +2961,7 @@ SILC_SERVER_CMD_FUNC(info)
     if (!entry && !cmd->pending && !server->standalone) {
       /* Send to the primary router */
       SilcBuffer tmpbuf;
-      uint16 old_ident;
+      SilcUInt16 old_ident;
 
       old_ident = silc_command_get_ident(cmd->payload);
       silc_command_set_ident(cmd->payload, ++server->cmd_ident);
@@ -2904,7 +2987,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;
   }
 
@@ -2915,7 +2998,7 @@ SILC_SERVER_CMD_FUNC(info)
 
   /* Send the reply */
   packet = silc_command_reply_payload_encode_va(SILC_COMMAND_INFO,
-                                               SILC_STATUS_OK, ident, 3,
+                                               SILC_STATUS_OK, 0, ident, 3,
                                                2, idp->data, idp->len,
                                                3, server_name, 
                                                strlen(server_name),
@@ -2939,7 +3022,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);
@@ -2948,7 +3031,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);
@@ -2958,10 +3041,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;
   }
 
@@ -2971,6 +3054,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. */
@@ -2981,20 +3160,23 @@ static void silc_server_command_join_channel(SilcServer server,
                                             SilcClientID *client_id,
                                             bool created,
                                             bool create_key,
-                                            uint32 umode,
+                                            SilcUInt32 umode,
                                             const unsigned char *auth,
-                                            uint32 auth_len)
+                                            SilcUInt32 auth_len)
 {
   SilcSocketConnection sock = cmd->sock;
   unsigned char *tmp;
-  uint32 tmp_len, user_count;
+  SilcUInt32 tmp_len, user_count;
   unsigned char *passphrase = NULL, mode[4], tmp2[4], tmp3[4];
   SilcClientEntry client;
   SilcChannelClientEntry chl;
   SilcBuffer reply, chidp, clidp, keyp = NULL, user_list, mode_list;
-  uint16 ident = silc_command_get_ident(cmd->payload);
+  SilcUInt16 ident = silc_command_get_ident(cmd->payload);
   char check[512], check2[512];
   bool founder = FALSE;
+  bool resolve;
+  unsigned char *fkey = NULL;
+  SilcUInt32 fkey_len = 0;
 
   SILC_LOG_DEBUG(("Start"));
 
@@ -3005,13 +3187,21 @@ 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;
 
-      /* The client info is being resolved. Reprocess this packet after
-        receiving the reply to the query. */
+      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,
                                  silc_server_command_join, 
@@ -3030,20 +3220,29 @@ static void silc_server_command_join_channel(SilcServer server,
    */
   if (auth && auth_len && channel->mode & SILC_CHANNEL_MODE_FOUNDER_AUTH) {
     SilcIDListData idata = (SilcIDListData)client;
+    SilcChannelClientEntry chl2;
+    SilcHashTableList htl;
 
     if (channel->founder_key && idata->public_key &&
        silc_pkcs_public_key_compare(channel->founder_key, 
                                     idata->public_key)) {
-      void *auth_data = (channel->founder_method == SILC_AUTH_PASSWORD ?
-                        (void *)channel->founder_passwd : 
-                        (void *)channel->founder_key);
-      uint32 auth_data_len = (channel->founder_method == SILC_AUTH_PASSWORD ?
-                             channel->founder_passwd_len : 0);
-
       /* Check whether the client is to become founder */
-      if (silc_auth_verify_data(auth, auth_len, channel->founder_method, 
-                               auth_data, auth_data_len,
-                               idata->hash, client->id, SILC_ID_CLIENT)) {
+      if (silc_auth_verify_data(auth, auth_len, SILC_AUTH_PUBLIC_KEY,
+                               channel->founder_key, 0, server->sha1hash,
+                               client->id, SILC_ID_CLIENT)) {
+
+       /* There cannot be anyone else as founder on the channel now.  This
+          client is definitely the founder due to this authentication */
+       silc_hash_table_list(channel->user_list, &htl);
+       while (silc_hash_table_get(&htl, NULL, (void *)&chl2))
+         if (chl2->mode & SILC_CHANNEL_UMODE_CHANFO) {
+           chl2->mode &= ~SILC_CHANNEL_UMODE_CHANFO;
+           silc_server_force_cumode_change(server, NULL, channel, chl2,
+                                           chl2->mode);
+           break;
+         }
+       silc_hash_table_list_reset(&htl);
+
        umode = (SILC_CHANNEL_UMODE_CHANOP | SILC_CHANNEL_UMODE_CHANFO);
        founder = TRUE;
       }
@@ -3083,7 +3282,7 @@ static void silc_server_command_join_channel(SilcServer server,
          (!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);
+                                             SILC_STATUS_ERR_NOT_INVITED, 0);
        goto out;
       }
     }
@@ -3096,7 +3295,7 @@ static void silc_server_command_join_channel(SilcServer server,
          silc_string_match(channel->ban_list, check2)) {
        silc_server_command_send_status_reply(
                                      cmd, SILC_COMMAND_JOIN,
-                                     SILC_STATUS_ERR_BANNED_FROM_CHANNEL);
+                                     SILC_STATUS_ERR_BANNED_FROM_CHANNEL, 0);
        goto out;
       }
     }
@@ -3106,7 +3305,8 @@ static void silc_server_command_join_channel(SilcServer server,
       if (silc_hash_table_count(channel->user_list) + 1 > 
          channel->user_limit) {
        silc_server_command_send_status_reply(cmd, SILC_COMMAND_JOIN,
-                                             SILC_STATUS_ERR_CHANNEL_IS_FULL);
+                                             SILC_STATUS_ERR_CHANNEL_IS_FULL,
+                                             0);
        goto out;
       }
     }
@@ -3116,15 +3316,13 @@ static void silc_server_command_join_channel(SilcServer server,
   if (channel->mode & SILC_CHANNEL_MODE_PASSPHRASE) {
     /* Get passphrase */
     tmp = silc_argument_get_arg_type(cmd->args, 3, &tmp_len);
-    if (tmp) {
-      passphrase = silc_calloc(tmp_len, sizeof(*passphrase));
-      memcpy(passphrase, tmp, tmp_len);
-    }
+    if (tmp)
+      passphrase = silc_memdup(tmp, tmp_len);
   
     if (!passphrase || !channel->passphrase ||
         memcmp(passphrase, channel->passphrase, strlen(channel->passphrase))) {
       silc_server_command_send_status_reply(cmd, SILC_COMMAND_JOIN,
-                                           SILC_STATUS_ERR_BAD_PASSWORD);
+                                           SILC_STATUS_ERR_BAD_PASSWORD, 0);
       goto out;
     }
   }
@@ -3134,9 +3332,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;
   }
 
@@ -3162,6 +3360,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,
@@ -3178,7 +3378,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,
@@ -3186,9 +3388,12 @@ static void silc_server_command_join_channel(SilcServer server,
     silc_free(tmp);
   }
 
+  if (channel->founder_key)
+    fkey = silc_pkcs_public_key_encode(channel->founder_key, &fkey_len);
+
   reply = 
     silc_command_reply_payload_encode_va(SILC_COMMAND_JOIN,
-                                        SILC_STATUS_OK, ident, 13,
+                                        SILC_STATUS_OK, 0, ident, 14,
                                         2, channel->channel_name,
                                         strlen(channel->channel_name),
                                         3, chidp->data, chidp->len,
@@ -3212,7 +3417,8 @@ static void silc_server_command_join_channel(SilcServer server,
                                         12, tmp3, 4,
                                         13, user_list->data, user_list->len,
                                         14, mode_list->data, 
-                                        mode_list->len);
+                                        mode_list->len,
+                                        15, fkey, fkey_len);
 
   /* Send command reply */
   silc_server_packet_send(server, sock, SILC_PACKET_COMMAND_REPLY, 0, 
@@ -3224,11 +3430,19 @@ static void silc_server_command_join_channel(SilcServer server,
      we'll ignore it (in packet_receive.c) so we must send it here. If
      we are router then this will send it to local clients and local
      servers. */
+  SILC_LOG_DEBUG(("Send JOIN notify to channel"));
   silc_server_send_notify_to_channel(server, NULL, channel, FALSE, 
                                     SILC_NOTIFY_TYPE_JOIN, 2,
                                     clidp->data, clidp->len,
                                     chidp->data, chidp->len);
 
+  /* Update statistics */
+  server->stat.my_chanclients++;
+  if (server->server_type == SILC_ROUTER) {
+    server->stat.cell_chanclients++;
+    server->stat.chanclients++;
+  }
+
   if (!cmd->pending) {
     /* Send JOIN notify packet to our primary router */
     if (!server->standalone)
@@ -3245,27 +3459,30 @@ static void silc_server_command_join_channel(SilcServer server,
        notify the mode change to the channel. */
     if (founder) {
       SILC_PUT32_MSB(chl->mode, mode);
+      SILC_LOG_DEBUG(("Send CUMODE_CHANGE notify to channel"));
       silc_server_send_notify_to_channel(server, NULL, channel, FALSE, 
-                                        SILC_NOTIFY_TYPE_CUMODE_CHANGE, 3,
+                                        SILC_NOTIFY_TYPE_CUMODE_CHANGE, 4,
                                         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);
+                                        mode, 4, clidp->data, clidp->len,
+                                        fkey, fkey_len);
     }
   }
 
+  /* Set CUMODE notify type to network */
+  if (founder && !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, channel->founder_key);
+
   silc_buffer_free(reply);
   silc_buffer_free(clidp);
   silc_buffer_free(chidp);
   silc_buffer_free(keyp);
   silc_buffer_free(user_list);
   silc_buffer_free(mode_list);
+  silc_free(fkey);
 
  out:
   silc_free(passphrase);
@@ -3279,10 +3496,10 @@ SILC_SERVER_CMD_FUNC(join)
   SilcServerCommandContext cmd = (SilcServerCommandContext)context;
   SilcServer server = cmd->server;
   unsigned char *auth;
-  uint32 tmp_len, auth_len;
+  SilcUInt32 tmp_len, auth_len;
   char *tmp, *channel_name = NULL, *cipher, *hmac;
   SilcChannelEntry channel;
-  uint32 umode = 0;
+  SilcUInt32 umode = 0;
   bool created = FALSE, create_key = TRUE;
   SilcClientID *client_id;
 
@@ -3292,17 +3509,18 @@ SILC_SERVER_CMD_FUNC(join)
   tmp = silc_argument_get_arg_type(cmd->args, 1, &tmp_len);
   if (!tmp) {
     silc_server_command_send_status_reply(cmd, SILC_COMMAND_JOIN,
-                                         SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
+                                         SILC_STATUS_ERR_NOT_ENOUGH_PARAMS,
+                                         0);
     goto out;
   }
   channel_name = tmp;
 
-  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;
   }
 
@@ -3310,13 +3528,15 @@ SILC_SERVER_CMD_FUNC(join)
   tmp = silc_argument_get_arg_type(cmd->args, 2, &tmp_len);
   if (!tmp) {
     silc_server_command_send_status_reply(cmd, SILC_COMMAND_JOIN,
-                                         SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
+                                         SILC_STATUS_ERR_NOT_ENOUGH_PARAMS,
+                                         0);
     goto out;
   }
-  client_id = silc_id_payload_parse_id(tmp, tmp_len);
+  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;
   }
 
@@ -3330,18 +3550,12 @@ SILC_SERVER_CMD_FUNC(join)
                                             channel_name, NULL);
 
   if (cmd->sock->type == SILC_SOCKET_TYPE_CLIENT) {
-    /* If this is coming from client the Client ID in the command packet must
-       be same as the client's ID. */
-    if (cmd->sock->type == SILC_SOCKET_TYPE_CLIENT) {
-      SilcClientEntry entry = (SilcClientEntry)cmd->sock->user_data;
-      if (!SILC_ID_CLIENT_COMPARE(entry->id, client_id)) {
-       silc_server_command_send_status_reply(cmd, SILC_COMMAND_JOIN,
-                                       SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
-       goto out;
-      }
-    }
+    SilcClientEntry entry = (SilcClientEntry)cmd->sock->user_data;
+    silc_free(client_id);
+    client_id = silc_id_dup(entry->id, SILC_ID_CLIENT);
 
-    if (!channel || channel->disabled) {
+    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 
@@ -3350,8 +3564,11 @@ SILC_SERVER_CMD_FUNC(join)
        channel = silc_server_create_new_channel(server, server->id, cipher, 
                                                 hmac, channel_name, TRUE);
        if (!channel) {
-         silc_server_command_send_status_reply(cmd, SILC_COMMAND_JOIN,
-                                       SILC_STATUS_ERR_UNKNOWN_ALGORITHM);
+         silc_server_command_send_status_reply(
+                                        cmd, SILC_COMMAND_JOIN,
+                                        SILC_STATUS_ERR_UNKNOWN_ALGORITHM,
+                                        0);
+         silc_free(client_id);
          goto out;
        }
        
@@ -3367,13 +3584,15 @@ 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
             client already in the command reply callback. */
-         if (cmd->pending)
+         if (cmd->pending) {
+           silc_free(client_id);
            goto out;
+         }
          
          old_ident = silc_command_get_ident(cmd->payload);
          silc_command_set_ident(cmd->payload, ++server->cmd_ident);
@@ -3391,6 +3610,9 @@ SILC_SERVER_CMD_FUNC(join)
                                      silc_server_command_join,
                                      silc_server_command_dup(cmd));
          cmd->pending = TRUE;
+          silc_command_set_ident(cmd->payload, old_ident);
+         silc_buffer_free(tmpbuf);
+         silc_free(client_id);
          goto out;
        }
        
@@ -3403,8 +3625,10 @@ SILC_SERVER_CMD_FUNC(join)
          channel = silc_server_create_new_channel(server, server->id, cipher, 
                                                   hmac, channel_name, TRUE);
          if (!channel) {
-           silc_server_command_send_status_reply(cmd, SILC_COMMAND_JOIN,
-                                      SILC_STATUS_ERR_UNKNOWN_ALGORITHM);
+           silc_server_command_send_status_reply(
+                                      cmd, SILC_COMMAND_JOIN,
+                                      SILC_STATUS_ERR_UNKNOWN_ALGORITHM, 0);
+           silc_free(client_id);
            goto out;
          }
 
@@ -3422,8 +3646,10 @@ SILC_SERVER_CMD_FUNC(join)
         something went wrong with the joining as the channel was not found.
         We can't do anything else but ignore this. */
       if (cmd->sock->type == SILC_SOCKET_TYPE_ROUTER ||
-         server->server_type != SILC_ROUTER)
+         server->server_type != SILC_ROUTER) {
+       silc_free(client_id);
        goto out;
+      }
       
       /* We are router and the channel does not seem exist so we will check
         our global list as well for the channel. */
@@ -3434,8 +3660,10 @@ SILC_SERVER_CMD_FUNC(join)
        channel = silc_server_create_new_channel(server, server->id, cipher, 
                                                 hmac, channel_name, TRUE);
        if (!channel) {
-         silc_server_command_send_status_reply(cmd, SILC_COMMAND_JOIN,
-                                      SILC_STATUS_ERR_UNKNOWN_ALGORITHM);
+         silc_server_command_send_status_reply(
+                                      cmd, SILC_COMMAND_JOIN,
+                                      SILC_STATUS_ERR_UNKNOWN_ALGORITHM, 0);
+         silc_free(client_id);
          goto out;
        }
 
@@ -3448,22 +3676,36 @@ 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_status(reply->payload, NULL, NULL) &&
+         channel->mode & SILC_CHANNEL_MODE_PASSPHRASE) {
+       /* Save channel passphrase, if user provided it successfully */
+       unsigned char *pa;
+       SilcUInt32 pa_len;
+       pa = silc_argument_get_arg_type(reply->args, 3, &pa_len);
+       if (pa) {
+         silc_free(channel->passphrase);
+         channel->passphrase = silc_memdup(pa, pa_len);
+       }
+      }
     }
 
-    if (silc_command_get(reply->payload) == SILC_COMMAND_WHOIS)
+    if (silc_command_get(reply->payload) == SILC_COMMAND_WHOIS &&
+       !channel->disabled && !silc_hash_table_count(channel->user_list))
       created = TRUE;
   }
 
   /* If the channel does not have global users and is also empty the client
      will be the channel founder and operator. */
-  if (!channel->global_users && !silc_hash_table_count(channel->user_list))
+  if (!channel->disabled &&
+      !channel->global_users && !silc_hash_table_count(channel->user_list))
     umode = (SILC_CHANNEL_UMODE_CHANOP | SILC_CHANNEL_UMODE_CHANFO);
 
   /* Join to the channel */
@@ -3486,8 +3728,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);
 
@@ -3495,7 +3737,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;
   }
 
@@ -3504,22 +3746,25 @@ 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);
     } 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);
     }
 
@@ -3542,7 +3787,7 @@ SILC_SERVER_CMD_FUNC(motd)
        entry && !entry->motd) {
       /* Send to the server */
       SilcBuffer tmpbuf;
-      uint16 old_ident;
+      SilcUInt16 old_ident;
 
       old_ident = silc_command_get_ident(cmd->payload);
       silc_command_set_ident(cmd->payload, ++server->cmd_ident);
@@ -3566,7 +3811,7 @@ SILC_SERVER_CMD_FUNC(motd)
     if (!entry && !cmd->pending && !server->standalone) {
       /* Send to the primary router */
       SilcBuffer tmpbuf;
-      uint16 old_ident;
+      SilcUInt16 old_ident;
 
       old_ident = silc_command_get_ident(cmd->payload);
       silc_command_set_ident(cmd->payload, ++server->cmd_ident);
@@ -3589,13 +3834,13 @@ SILC_SERVER_CMD_FUNC(motd)
 
     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);
     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, entry->motd,
                                                  entry->motd ? 
@@ -3620,71 +3865,74 @@ 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;
-  }
+    /* Update statistics */
+    if (mask & SILC_UMODE_GONE) {
+      if (!(client->mode & SILC_UMODE_GONE))
+       server->stat.my_aways++;
+    } else {
+      if (client->mode & SILC_UMODE_GONE)
+       server->stat.my_aways--;
+    }
 
-  /* Send UMODE change to primary router */
-  if (!server->standalone)
-    silc_server_send_notify_umode(server, server->router->connection, TRUE,
-                                 client->id, client->mode);
+    /* 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);
+
+    /* 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);
@@ -3693,75 +3941,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)
@@ -3770,39 +3949,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, *passphrase = NULL;
-  uint32 mode_mask, tmp_len, tmp_len2;
-  uint16 ident = silc_command_get_ident(cmd->payload);
+  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, 
@@ -3812,25 +3993,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;
   }
 
@@ -3848,32 +4046,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 {
@@ -3894,20 +4092,18 @@ 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 */
-      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;
     }
   }
 
@@ -3920,14 +4116,14 @@ SILC_SERVER_CMD_FUNC(cmode)
       cipher = silc_argument_get_arg_type(cmd->args, 5, NULL);
       if (!cipher) {
        silc_server_command_send_status_reply(cmd, SILC_COMMAND_CMODE,
-                                  SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
+                                  SILC_STATUS_ERR_NOT_ENOUGH_PARAMS, 0);
        goto out;
       }
 
       /* Delete old cipher and allocate the new one */
       if (!silc_cipher_alloc(cipher, &newkey)) {
        silc_server_command_send_status_reply(cmd, SILC_COMMAND_CMODE,
-                                      SILC_STATUS_ERR_UNKNOWN_ALGORITHM);
+                                      SILC_STATUS_ERR_UNKNOWN_ALGORITHM, 0);
        goto out;
       }
 
@@ -3960,7 +4156,7 @@ SILC_SERVER_CMD_FUNC(cmode)
       /* Delete old cipher and allocate default one */
       if (!silc_cipher_alloc(cipher ? cipher : SILC_DEFAULT_CIPHER, &newkey)) {
        silc_server_command_send_status_reply(cmd, SILC_COMMAND_CMODE,
-                                  SILC_STATUS_ERR_UNKNOWN_ALGORITHM);
+                                  SILC_STATUS_ERR_UNKNOWN_ALGORITHM, 0);
        goto out;
       }
 
@@ -3995,14 +4191,14 @@ SILC_SERVER_CMD_FUNC(cmode)
       hmac = silc_argument_get_arg_type(cmd->args, 6, NULL);
       if (!hmac) {
        silc_server_command_send_status_reply(cmd, SILC_COMMAND_CMODE,
-                                  SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
+                                  SILC_STATUS_ERR_NOT_ENOUGH_PARAMS, 0);
        goto out;
       }
 
       /* Delete old hmac and allocate the new one */
       if (!silc_hmac_alloc(hmac, NULL, &newhmac)) {
        silc_server_command_send_status_reply(cmd, SILC_COMMAND_CMODE,
-                                      SILC_STATUS_ERR_UNKNOWN_ALGORITHM);
+                                      SILC_STATUS_ERR_UNKNOWN_ALGORITHM, 0);
        goto out;
       }
 
@@ -4029,7 +4225,7 @@ SILC_SERVER_CMD_FUNC(cmode)
       silc_hmac_free(channel->hmac);
       if (!silc_hmac_alloc(hmac ? hmac : SILC_DEFAULT_HMAC, NULL, &newhmac)) {
        silc_server_command_send_status_reply(cmd, SILC_COMMAND_CMODE,
-                                      SILC_STATUS_ERR_UNKNOWN_ALGORITHM);
+                                      SILC_STATUS_ERR_UNKNOWN_ALGORITHM, 0);
        goto out;
       }
 
@@ -4051,47 +4247,41 @@ 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, server->sha1hash,
+                                  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;
-         }
-       }
+       channel->founder_key = silc_pkcs_public_key_copy(idata->public_key);
+        if (!channel->founder_key) {
+         silc_server_command_send_status_reply(cmd, SILC_COMMAND_CMODE,
+                                               SILC_STATUS_ERR_AUTH_FAILED,
+                                               0);
+         goto out;
+        }
 
-       silc_auth_payload_free(auth);
+       founder_key = channel->founder_key;
+       fkey = silc_pkcs_public_key_encode(founder_key, &fkey_len);
+        if (!fkey) {
+         silc_server_command_send_status_reply(cmd, SILC_COMMAND_CMODE,
+                                               SILC_STATUS_ERR_AUTH_FAILED,
+                                               0);
+         goto out;
+        }
       }
     }
   } else {
@@ -4099,10 +4289,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;
       }
     }
   }
@@ -4113,13 +4300,14 @@ SILC_SERVER_CMD_FUNC(cmode)
   /* Send CMODE_CHANGE notify. */
   cidp = silc_id_payload_encode(client->id, SILC_ID_CLIENT);
   silc_server_send_notify_to_channel(server, NULL, channel, FALSE,
-                                    SILC_NOTIFY_TYPE_CMODE_CHANGE, 5,
+                                    SILC_NOTIFY_TYPE_CMODE_CHANGE, 6,
                                     cidp->data, cidp->len, 
                                     tmp_mask, 4,
                                     cipher, cipher ? strlen(cipher) : 0,
                                     hmac, hmac ? strlen(hmac) : 0,
                                     passphrase, passphrase ? 
-                                    strlen(passphrase) : 0);
+                                    strlen(passphrase) : 0,
+                                    fkey, fkey_len);
 
   /* Set CMODE notify type to network */
   if (!server->standalone)
@@ -4127,21 +4315,22 @@ SILC_SERVER_CMD_FUNC(cmode)
                                  server->server_type == SILC_ROUTER ? 
                                  TRUE : FALSE, channel,
                                  mode_mask, client->id, SILC_ID_CLIENT,
-                                 cipher, hmac, passphrase);
+                                 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_buffer_free(cidp);
 
  out:
+  silc_free(fkey);
+  silc_free(channel_id);
   silc_server_command_free(cmd);
 }
 
@@ -4160,9 +4349,12 @@ 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);
+  SilcPublicKey founder_key = NULL;
+  unsigned char *fkey = NULL;
+  SilcUInt32 fkey_len = 0;
 
   SILC_SERVER_COMMAND_CHECK(SILC_COMMAND_CUMODE, cmd, 3, 4);
 
@@ -4170,13 +4362,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;
   }
 
@@ -4188,27 +4380,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);
@@ -4217,13 +4408,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;
   }
 
@@ -4239,21 +4430,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);
   }
 
   /* 
@@ -4262,56 +4449,74 @@ 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;
+      SilcChannelClientEntry chl2;
+      SilcHashTableList htl;
 
       if (!(channel->mode & SILC_CHANNEL_MODE_FOUNDER_AUTH) ||
          !channel->founder_key || !idata->public_key ||
          !silc_pkcs_public_key_compare(channel->founder_key, 
                                        idata->public_key)) {
        silc_server_command_send_status_reply(cmd, SILC_COMMAND_CUMODE,
-                                             SILC_STATUS_ERR_NOT_YOU);
+                                             SILC_STATUS_ERR_AUTH_FAILED, 0);
        goto out;
       }
 
       tmp_auth = silc_argument_get_arg_type(cmd->args, 4, &tmp_auth_len);
       if (!tmp_auth) {
        silc_server_command_send_status_reply(cmd, SILC_COMMAND_CUMODE,
-                                    SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
+                                             SILC_STATUS_ERR_AUTH_FAILED, 0);
        goto out;
       }
 
-      auth = (channel->founder_method == SILC_AUTH_PASSWORD ?
-             (void *)channel->founder_passwd : (void *)channel->founder_key);
-      auth_len = (channel->founder_method == SILC_AUTH_PASSWORD ?
-                 channel->founder_passwd_len : 0);
-      
-      if (!silc_auth_verify_data(tmp_auth, tmp_auth_len,
-                                channel->founder_method, auth, auth_len,
-                                idata->hash, client->id, SILC_ID_CLIENT)) {
+      /* Verify the authentication payload */
+      if (!silc_auth_verify_data(tmp_auth, tmp_auth_len, SILC_AUTH_PUBLIC_KEY,
+                                channel->founder_key, 0, server->sha1hash,
+                                client->id, SILC_ID_CLIENT)) {
        silc_server_command_send_status_reply(cmd, SILC_COMMAND_CUMODE,
-                                             SILC_STATUS_ERR_AUTH_FAILED);
+                                             SILC_STATUS_ERR_AUTH_FAILED, 0);
        goto out;
       }
-      
-      sender_mask = chl->mode |= SILC_CHANNEL_UMODE_CHANFO;
+
       notify = TRUE;
+      founder_key = channel->founder_key;
+      fkey = silc_pkcs_public_key_encode(founder_key, &fkey_len);
+      if (!fkey) {
+       silc_server_command_send_status_reply(cmd, SILC_COMMAND_CUMODE,
+                                             SILC_STATUS_ERR_AUTH_FAILED, 0);
+       goto out;
+      }
+
+      /* There cannot be anyone else as founder on the channel now.  This
+        client is definitely the founder due to this authentication */
+      silc_hash_table_list(channel->user_list, &htl);
+      while (silc_hash_table_get(&htl, NULL, (void *)&chl2))
+       if (chl2->mode & SILC_CHANNEL_UMODE_CHANFO) {
+         chl2->mode &= ~SILC_CHANNEL_UMODE_CHANFO;
+         silc_server_force_cumode_change(server, NULL, channel, chl2,
+                                         chl2->mode);
+         break;
+       }
+      silc_hash_table_list_reset(&htl);
+
+      sender_mask = chl->mode |= SILC_CHANNEL_UMODE_CHANFO;
     }
   } else {
     if (chl->mode & SILC_CHANNEL_UMODE_CHANFO) {
@@ -4321,7 +4526,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;
       }
     }
@@ -4330,11 +4535,12 @@ SILC_SERVER_CMD_FUNC(cumode)
   if (target_mask & SILC_CHANNEL_UMODE_CHANOP) {
     /* Promote to operator */
     if (!(chl->mode & SILC_CHANNEL_UMODE_CHANOP)) {
-      if (!(sender_mask & SILC_CHANNEL_UMODE_CHANOP) &&
-         !(sender_mask & SILC_CHANNEL_UMODE_CHANFO)) {
-       silc_server_command_send_status_reply(cmd, SILC_COMMAND_CUMODE,
-                                             SILC_STATUS_ERR_NO_CHANNEL_PRIV);
-       goto out;
+      if (!(sender_mask & SILC_CHANNEL_UMODE_CHANOP) && 
+          !(sender_mask & SILC_CHANNEL_UMODE_CHANFO)) {
+        silc_server_command_send_status_reply(cmd, SILC_COMMAND_CUMODE,
+                                              SILC_STATUS_ERR_NO_CHANNEL_PRIV,
+                                              0);
+        goto out;
       }
 
       chl->mode |= SILC_CHANNEL_UMODE_CHANOP;
@@ -4343,14 +4549,109 @@ SILC_SERVER_CMD_FUNC(cumode)
   } else {
     if (chl->mode & SILC_CHANNEL_UMODE_CHANOP) {
       if (!(sender_mask & SILC_CHANNEL_UMODE_CHANOP) &&
-         !(sender_mask & SILC_CHANNEL_UMODE_CHANFO)) {
+          !(sender_mask & SILC_CHANNEL_UMODE_CHANFO)) {
+        silc_server_command_send_status_reply(cmd, SILC_COMMAND_CUMODE,   
+                                              SILC_STATUS_ERR_NO_CHANNEL_PRIV,
+                                              0);
+        goto out;
+      }
+      
+      /* Demote to normal user */
+      chl->mode &= ~SILC_CHANNEL_UMODE_CHANOP;
+      notify = TRUE;
+    }
+  }
+
+  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_NO_CHANNEL_PRIV);
+                                             SILC_STATUS_ERR_NOT_YOU, 0);
        goto out;
       }
 
-      /* Demote to normal user */
-      chl->mode &= ~SILC_CHANNEL_UMODE_CHANOP;
+      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;
+    }
+  }
+
+  if (target_mask & SILC_CHANNEL_UMODE_QUIET) {
+    if (!(chl->mode & SILC_CHANNEL_UMODE_QUIET)) {
+      if (client == target_client) {
+       silc_server_command_send_status_reply(cmd, SILC_COMMAND_CUMODE,
+                                             SILC_STATUS_ERR_PERM_DENIED, 0);
+       goto out;
+      }
+      chl->mode |= SILC_CHANNEL_UMODE_QUIET;
+      notify = TRUE;
+    }
+  } else {
+    if (chl->mode & SILC_CHANNEL_UMODE_QUIET) {
+      if (client == target_client) {
+       silc_server_command_send_status_reply(cmd, SILC_COMMAND_CUMODE,
+                                             SILC_STATUS_ERR_PERM_DENIED, 0);
+       goto out;
+      }
+      chl->mode &= ~SILC_CHANNEL_UMODE_QUIET;
       notify = TRUE;
     }
   }
@@ -4361,10 +4662,11 @@ SILC_SERVER_CMD_FUNC(cumode)
   /* Send notify to channel, notify only if mode was actually changed. */
   if (notify) {
     silc_server_send_notify_to_channel(server, NULL, channel, FALSE, 
-                                      SILC_NOTIFY_TYPE_CUMODE_CHANGE, 3,
+                                      SILC_NOTIFY_TYPE_CUMODE_CHANGE, 4,
                                       idp->data, idp->len,
                                       tmp_mask, 4, 
-                                      tmp_id, tmp_len);
+                                      tmp_id, tmp_len,
+                                      fkey, fkey_len);
 
     /* Set CUMODE notify type to network */
     if (!server->standalone)
@@ -4373,12 +4675,12 @@ SILC_SERVER_CMD_FUNC(cumode)
                                     TRUE : FALSE, channel,
                                     target_mask, client->id, 
                                     SILC_ID_CLIENT,
-                                    target_client->id);
+                                    target_client->id, founder_key);
   }
 
   /* 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);
@@ -4391,6 +4693,7 @@ SILC_SERVER_CMD_FUNC(cumode)
   silc_buffer_free(idp);
 
  out:
+  silc_free(fkey);
   silc_server_command_free(cmd);
 }
 
@@ -4407,7 +4710,7 @@ SILC_SERVER_CMD_FUNC(kick)
   SilcChannelEntry channel;
   SilcChannelClientEntry chl;
   SilcBuffer idp;
-  uint32 tmp_len, target_idp_len;
+  SilcUInt32 tmp_len, target_idp_len;
   unsigned char *tmp, *comment, *target_idp;
 
   SILC_SERVER_COMMAND_CHECK(SILC_COMMAND_LEAVE, cmd, 1, 3);
@@ -4416,13 +4719,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;
   }
 
@@ -4434,23 +4737,24 @@ 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;
   }
   
@@ -4458,13 +4762,13 @@ SILC_SERVER_CMD_FUNC(kick)
   target_idp = silc_argument_get_arg_type(cmd->args, 2, &target_idp_len);
   if (!target_idp) {
     silc_server_command_send_status_reply(cmd, SILC_COMMAND_KICK,
-                                         SILC_STATUS_ERR_NO_CLIENT_ID);
+                                         SILC_STATUS_ERR_NO_CLIENT_ID, 0);
     goto out;
   }
-  client_id = silc_id_payload_parse_id(target_idp, target_idp_len);
+  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;
   }
 
@@ -4476,22 +4780,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);
@@ -4500,7 +4805,7 @@ SILC_SERVER_CMD_FUNC(kick)
 
   /* Send command reply to sender */
   silc_server_command_send_status_reply(cmd, SILC_COMMAND_KICK, 
-                                       SILC_STATUS_OK);
+                                       SILC_STATUS_OK, 0);
 
   /* Send KICKED notify to local clients on the channel */
   idp = silc_id_payload_encode(client->id, SILC_ID_CLIENT);
@@ -4523,7 +4828,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 */
@@ -4550,9 +4855,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);
 
@@ -4563,19 +4870,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;
     }
   }
@@ -4584,163 +4893,434 @@ 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_SERVER_CMD_FUNC(silcoper)
+SILC_TASK_CALLBACK(silc_server_command_detach_cb)
 {
-  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 = (QuitInternal)context;
+  SilcClientEntry client = (SilcClientEntry)q->sock->user_data;
 
-  SILC_SERVER_COMMAND_CHECK(SILC_COMMAND_SILCOPER, cmd, 1, 2);
+  /* 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);
 
-  if (!client || cmd->sock->type != SILC_SOCKET_TYPE_CLIENT)
-    goto out;
+  /* Close the connection on our side */
+  client->router = NULL;
+  client->connection = NULL;
+  q->sock->user_data = NULL;
+  silc_server_close_connection(q->server, q->sock);
 
-  if (server->server_type != SILC_ROUTER) {
-    silc_server_command_send_status_reply(cmd, SILC_COMMAND_SILCOPER,
-                                         SILC_STATUS_ERR_AUTH_FAILED);
-    goto out;
+  silc_free(q);
+}
+
+SILC_TASK_CALLBACK(silc_server_command_detach_timeout)
+{
+  QuitInternal q = (QuitInternal)context;
+  SilcClientID *client_id = (SilcClientID *)q->sock;
+  SilcClientEntry client;
+
+  SILC_LOG_DEBUG(("Start"));
+
+  client = silc_idlist_find_client_by_id(q->server->local_list, client_id,
+                                        FALSE, NULL);
+
+  if (client && client->mode & SILC_UMODE_DETACHED)
+    silc_server_free_client_data(q->server, NULL, client, TRUE,
+                                "Detach timeout");
+
+  silc_free(client_id);
+  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;
+  QuitInternal q;
+
+  if (server->config->detach_disabled) {
+    silc_server_command_send_status_reply(cmd, SILC_COMMAND_DETACH,
+                                         SILC_STATUS_ERR_UNKNOWN_COMMAND, 0);
+    goto out;
   }
 
-  /* 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);
+  if (cmd->sock->type != SILC_SOCKET_TYPE_CLIENT)
     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);
+  server->stat.my_detached++;
+
+  /* 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 = silc_id_dup(client->id, SILC_ID_CLIENT);
+    silc_schedule_task_add(server->schedule, 0, 
+                          silc_server_command_detach_timeout,
+                          q, server->config->detach_timeout * 60,
+                          0, SILC_TASK_TIMEOUT, SILC_TASK_PRI_LOW);
   }
 
-  /* 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;
+  /* 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 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);
+  /* 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;
   }
 
-  /* 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);
+  /* 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;
   }
 
-  /* Client is now router operator */
-  client->mode |= SILC_UMODE_ROUTER_OPERATOR;
+  /* 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;
+  }
 
-  /* Send UMODE change to primary router */
-  if (!server->standalone)
-    silc_server_send_notify_umode(server, server->router->connection, TRUE,
-                                 client->id, client->mode);
+  if (add_nick && add_nick_len > 128)
+    add_nick[128] = '\0';
+  if (del_nick && del_nick_len > 128)
+    del_nick[128] = '\0';
 
-  /* Send reply to the sender */
-  silc_server_command_send_status_reply(cmd, SILC_COMMAND_SILCOPER,
-                                       SILC_STATUS_OK);
+  memset(nick, 0, sizeof(nick));
+
+  /* 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);
+  }
+
+  /* Distribute the watch list to backup routers too */
+  if (server->backup) {
+    SilcBuffer tmpbuf;
+    silc_command_set_ident(cmd->payload, ++server->cmd_ident);
+    tmpbuf = silc_command_payload_encode_payload(cmd->payload);
+    silc_server_backup_send(server, NULL, SILC_PACKET_COMMAND,
+                           cmd->packet->flags, tmpbuf->data, tmpbuf->len,
+                           FALSE, TRUE);
+    silc_buffer_free(tmpbuf);
+  }
+
+  silc_server_command_send_status_reply(cmd, SILC_COMMAND_WATCH,
+                                       SILC_STATUS_OK, 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);
@@ -4759,8 +5339,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;
@@ -4770,10 +5350,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;
     }
   }
@@ -4787,25 +5367,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;
   }
 
@@ -4857,11 +5435,11 @@ SILC_SERVER_CMD_FUNC(ban)
   /* Send the reply back to the client */
   packet = 
     silc_command_reply_payload_encode_va(SILC_COMMAND_BAN,
-                                        SILC_STATUS_OK, ident, 2,
+                                        SILC_STATUS_OK, 0, ident, 2,
                                         2, id, id_len,
                                         3, channel->ban_list, 
                                         channel->ban_list ? 
-                                        strlen(channel->ban_list) - 1 : 0);
+                                        strlen(channel->ban_list) -1 : 0);
   silc_server_packet_send(server, cmd->sock, SILC_PACKET_COMMAND_REPLY, 0, 
                          packet->data, packet->len, FALSE);
     
@@ -4872,111 +5450,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, 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_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)
@@ -4987,7 +5460,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);
@@ -4996,13 +5469,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;
   }
 
@@ -5012,15 +5485,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;
   }
 
@@ -5031,8 +5505,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,
@@ -5068,12 +5542,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);
@@ -5086,15 +5560,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;
     }
   }
@@ -5108,7 +5582,8 @@ SILC_SERVER_CMD_FUNC(users)
     channel = silc_idlist_find_channel_by_name(server->local_list, 
                                               channel_name, NULL);
 
-  if (!channel || channel->disabled) {
+  if (!channel || (!server->standalone && (channel->disabled || 
+                   !channel->users_resolved))) {
     if (server->server_type != SILC_ROUTER && !server->standalone &&
        !cmd->pending) {
       SilcBuffer tmpbuf;
@@ -5142,32 +5617,31 @@ 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_client_on_channel(cmd->sock->user_data, channel, 
+                                         NULL)) {
       silc_server_command_send_status_reply(cmd, SILC_COMMAND_USERS,
-                                           SILC_STATUS_ERR_NO_SUCH_CHANNEL);
-      goto out;
-    }
-  } else {
-    if (channel->mode & 
-       (SILC_CHANNEL_MODE_PRIVATE | SILC_CHANNEL_MODE_SECRET)) {
-      silc_server_command_send_status_reply(cmd, SILC_COMMAND_USERS,
-                                           SILC_STATUS_ERR_NO_SUCH_CHANNEL);
+                                           SILC_STATUS_ERR_NOT_ON_CHANNEL, 0);
       goto out;
     }
   }
 
   /* Get the users list */
-  silc_server_get_users_on_channel(server, channel, &client_id_list,
-                                  &client_mode_list, &list_count);
+  if (!silc_server_get_users_on_channel(server, channel, &client_id_list,
+                                       &client_mode_list, &list_count)) {
+    list_count = 0;
+    client_id_list = NULL;
+    client_mode_list = NULL;
+  }
 
   /* List count */
   SILC_PUT32_MSB(list_count, lc);
@@ -5175,20 +5649,26 @@ 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,
-                                               client_id_list->len,
-                                               5, client_mode_list->data,
-                                               client_mode_list->len);
+                                               4, client_id_list ? 
+                                               client_id_list->data : NULL,
+                                               client_id_list ?
+                                               client_id_list->len : 0,
+                                               5, client_mode_list ?
+                                               client_mode_list->data : NULL,
+                                               client_mode_list ?
+                                               client_mode_list->len : 0);
   silc_server_packet_send(server, cmd->sock, SILC_PACKET_COMMAND_REPLY, 0, 
                          packet->data, packet->len, FALSE);
     
   silc_buffer_free(idp);
   silc_buffer_free(packet);
-  silc_buffer_free(client_id_list);
-  silc_buffer_free(client_mode_list);
+  if (client_id_list)
+    silc_buffer_free(client_id_list);
+  if (client_mode_list)
+    silc_buffer_free(client_mode_list);
   silc_free(id);
 
  out:
@@ -5208,24 +5688,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(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;
   }
 
@@ -5242,14 +5725,15 @@ 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;
       
@@ -5274,18 +5758,20 @@ SILC_SERVER_CMD_FUNC(getkey)
 
     if (!client) {
       silc_server_command_send_status_reply(cmd, SILC_COMMAND_GETKEY,
-                                           SILC_STATUS_ERR_NO_SUCH_CLIENT_ID);
+                                           SILC_STATUS_ERR_NO_SUCH_CLIENT_ID,
+                                           0);
       goto out;
     }
 
     /* 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,
@@ -5315,7 +5801,7 @@ SILC_SERVER_CMD_FUNC(getkey)
         (server_entry && !server_entry->data.public_key && !cmd->pending &&
          !server->standalone))) {
       SilcBuffer tmpbuf;
-      uint16 old_ident;
+      SilcUInt16 old_ident;
       
       old_ident = silc_command_get_ident(cmd->payload);
       silc_command_set_ident(cmd->payload, ++server->cmd_ident);
@@ -5331,7 +5817,6 @@ SILC_SERVER_CMD_FUNC(getkey)
                                  silc_server_command_getkey,
                                  silc_server_command_dup(cmd));
       cmd->pending = TRUE;
-      
       silc_command_set_ident(cmd->payload, old_ident);
       silc_buffer_free(tmpbuf);
       goto out;
@@ -5339,17 +5824,20 @@ SILC_SERVER_CMD_FUNC(getkey)
 
     if (!server_entry) {
       silc_server_command_send_status_reply(cmd, SILC_COMMAND_GETKEY,
-                                           SILC_STATUS_ERR_NO_SUCH_SERVER_ID);
+                                           SILC_STATUS_ERR_NO_SUCH_SERVER_ID,
+                                           0);
       goto out;
     }
 
     /* 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,
@@ -5367,7 +5855,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);
@@ -5385,3 +5873,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);
+}