updates.
[silc.git] / apps / silcd / command.c
index 02ec6d1d6aefaf07681f48562db78c15108bf5f7..184382eaf0b3c14a34332b375cee4fa8d9468390 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
@@ -56,8 +56,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 +66,20 @@ 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(silcoper, SILCOPER,
                  SILC_CF_LAG | SILC_CF_REG | SILC_CF_SILC_OPER),
   SILC_SERVER_CMD(leave, LEAVE, SILC_CF_LAG_STRICT | SILC_CF_REG),
   SILC_SERVER_CMD(users, USERS, SILC_CF_LAG | SILC_CF_REG),
   SILC_SERVER_CMD(getkey, GETKEY, SILC_CF_LAG | SILC_CF_REG),
 
+  SILC_SERVER_CMD(connect, PRIV_CONNECT, 
+                 SILC_CF_LAG | SILC_CF_REG | SILC_CF_OPER),
+  SILC_SERVER_CMD(close, PRIV_CLOSE,
+                 SILC_CF_LAG | SILC_CF_REG | SILC_CF_OPER),
+  SILC_SERVER_CMD(shutdown, PRIV_SHUTDOWN, SILC_CF_LAG | SILC_CF_REG | 
+                 SILC_CF_OPER),
+
   { NULL, 0 },
 };
 
@@ -89,7 +92,7 @@ SilcServerCommand silc_command_list[] =
    of arguments. */
 #define SILC_SERVER_COMMAND_CHECK(command, context, min, max)                \
 do {                                                                         \
-  SilcUInt32 _argc;                                                                  \
+  SilcUInt32 _argc;                                                          \
                                                                              \
   SILC_LOG_DEBUG(("Start"));                                                 \
                                                                              \
@@ -205,7 +208,7 @@ void silc_server_command_process(SilcServer server,
     if (cmd->cmd == command)
       break;
 
-  if (cmd == NULL) {
+  if (!cmd || !cmd->cb) {
     silc_server_command_send_status_reply(ctx, command,
                                          SILC_STATUS_ERR_UNKNOWN_COMMAND);
     silc_server_command_free(ctx);
@@ -393,7 +396,7 @@ silc_server_command_send_status_reply(SilcServerCommandContext cmd,
   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),
                                         0);
   silc_server_packet_send(cmd->server, cmd->sock,
@@ -418,7 +421,7 @@ silc_server_command_send_status_data(SilcServerCommandContext cmd,
   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,
@@ -437,16 +440,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 */
@@ -500,7 +497,7 @@ silc_server_command_whois_parse(SilcServerCommandContext cmd,
        has more than one ID set - take them all. */
 
     *client_id = silc_calloc(1, sizeof(**client_id));
-    (*client_id)[0] = silc_id_payload_parse_id(tmp, len);
+    (*client_id)[0] = silc_id_payload_parse_id(tmp, len, NULL);
     if ((*client_id)[0] == NULL) {
       silc_free(*client_id);
       silc_server_command_send_status_reply(cmd, command,
@@ -516,7 +513,7 @@ silc_server_command_whois_parse(SilcServerCommandContext cmd,
        if (tmp) {
          *client_id = silc_realloc(*client_id, sizeof(**client_id) *
                                    (*client_id_count + 1));
-         (*client_id)[k] = silc_id_payload_parse_id(tmp, len);
+         (*client_id)[k] = silc_id_payload_parse_id(tmp, len, NULL);
          if ((*client_id)[k] == NULL) {
            /* Cleanup all and fail */
            for (i = 0; i < *client_id_count; i++)
@@ -704,7 +701,7 @@ silc_server_command_whois_send_reply(SilcServerCommandContext cmd,
   SilcServer server = cmd->server;
   char *tmp;
   int i, k, len, valid_count;
-  SilcBuffer packet, idp, channels;
+  SilcBuffer packet, idp, channels, umode_list = NULL;
   SilcClientEntry entry;
   SilcCommandStatus status;
   SilcUInt16 ident = silc_command_get_ident(cmd->payload);
@@ -785,7 +782,12 @@ silc_server_command_whois_send_reply(SilcServerCommandContext cmd,
       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;
@@ -800,7 +802,7 @@ silc_server_command_whois_send_reply(SilcServerCommandContext cmd,
 
     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),
@@ -811,7 +813,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);
@@ -820,6 +825,10 @@ 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;
+    }
 
     k++;
   }
@@ -1158,7 +1167,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),
@@ -1817,7 +1826,7 @@ 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 {
@@ -1830,7 +1839,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));
@@ -1868,7 +1877,7 @@ silc_server_command_identify_send_reply(SilcServerCommandContext cmd,
       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 ? 
@@ -1905,7 +1914,7 @@ silc_server_command_identify_send_reply(SilcServerCommandContext cmd,
       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 ? 
@@ -2017,6 +2026,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);
+      goto out;
+    }
     snprintf(&nick[strlen(nick) - 1], 1, "%d", nickfail);
   }
 
@@ -2027,7 +2041,7 @@ SILC_SERVER_CMD_FUNC(nick)
     silc_server_send_notify_nick_change(server, server->router->connection, 
                                        server->server_type == SILC_SERVER ? 
                                        FALSE : TRUE, client->id,
-                                       new_id);
+                                       new_id, nick);
 
   oidp = silc_id_payload_encode(client->id, SILC_ID_CLIENT);
 
@@ -2051,14 +2065,16 @@ 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));
 
  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, 
+                                               SILC_STATUS_OK, 0, ident, 1, 
                                                2, nidp->data, nidp->len);
   silc_server_packet_send(cmd->server, cmd->sock, SILC_PACKET_COMMAND_REPLY,
                          0, packet->data, packet->len, FALSE);
@@ -2133,7 +2149,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),
@@ -2172,7 +2188,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),
@@ -2230,7 +2246,7 @@ 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);
@@ -2284,7 +2300,7 @@ SILC_SERVER_CMD_FUNC(topic)
                                          SILC_STATUS_ERR_NO_CHANNEL_ID);
     goto out;
   }
-  channel_id = silc_id_payload_parse_id(tmp, tmp_len);
+  channel_id = silc_id_payload_parse_id(tmp, tmp_len, NULL);
   if (!channel_id) {
     silc_server_command_send_status_reply(cmd, SILC_COMMAND_TOPIC,
                                          SILC_STATUS_ERR_NO_CHANNEL_ID);
@@ -2320,19 +2336,17 @@ SILC_SERVER_CMD_FUNC(topic)
     }
 
     /* See whether the client is on channel and has rights to change topic */
-    if (!silc_hash_table_find(channel->user_list, client, NULL, 
-                             (void *)&chl)) {
+    if (!silc_server_client_on_channel(client, channel, &chl)) {
       silc_server_command_send_status_reply(cmd, SILC_COMMAND_TOPIC,
                                            SILC_STATUS_ERR_NOT_ON_CHANNEL);
       goto out;
     }
 
-    if (chl->mode == SILC_CHANNEL_UMODE_NONE) {
-      if (channel->mode & SILC_CHANNEL_MODE_TOPIC) {
-       silc_server_command_send_status_reply(cmd, SILC_COMMAND_TOPIC,
-                                             SILC_STATUS_ERR_NO_CHANNEL_PRIV);
-       goto out;
-      }
+    if (chl->mode == SILC_CHANNEL_UMODE_NONE && 
+       channel->mode & SILC_CHANNEL_MODE_TOPIC) {
+      silc_server_command_send_status_reply(cmd, SILC_COMMAND_TOPIC,
+                                           SILC_STATUS_ERR_NO_CHANNEL_PRIV);
+      goto out;
     }
 
     /* Set the topic for channel */
@@ -2360,7 +2374,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 ? 
@@ -2404,7 +2418,7 @@ SILC_SERVER_CMD_FUNC(invite)
                                          SILC_STATUS_ERR_NO_CHANNEL_ID);
     goto out;
   }
-  channel_id = silc_id_payload_parse_id(tmp, len);
+  channel_id = silc_id_payload_parse_id(tmp, len, NULL);
   if (!channel_id) {
     silc_server_command_send_status_reply(cmd, SILC_COMMAND_INVITE,
                                          SILC_STATUS_ERR_NO_CHANNEL_ID);
@@ -2426,7 +2440,7 @@ SILC_SERVER_CMD_FUNC(invite)
 
   /* Check whether the sender of this command is on the channel. */
   sender = (SilcClientEntry)sock->user_data;
-  if (!silc_server_client_on_channel(sender, channel)) {
+  if (!silc_server_client_on_channel(sender, channel, &chl)) {
     silc_server_command_send_status_reply(cmd, SILC_COMMAND_INVITE,
                                          SILC_STATUS_ERR_NOT_ON_CHANNEL);
     goto out;
@@ -2434,13 +2448,11 @@ SILC_SERVER_CMD_FUNC(invite)
 
   /* Check whether the channel is invite-only channel. If yes then the
      sender of this command must be at least channel operator. */
-  if (channel->mode & SILC_CHANNEL_MODE_INVITE) {
-    silc_hash_table_find(channel->user_list, sender, NULL, (void *)&chl);
-    if (chl->mode == SILC_CHANNEL_UMODE_NONE) {
-      silc_server_command_send_status_reply(cmd, SILC_COMMAND_INVITE,
+  if (chl->mode == SILC_CHANNEL_UMODE_NONE &&
+      channel->mode & SILC_CHANNEL_MODE_INVITE) {
+    silc_server_command_send_status_reply(cmd, SILC_COMMAND_INVITE,
                                            SILC_STATUS_ERR_NO_CHANNEL_PRIV);
-      goto out;
-    }
+    goto out;
   }
 
   /* Get destination client ID */
@@ -2449,7 +2461,7 @@ SILC_SERVER_CMD_FUNC(invite)
     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);
@@ -2457,7 +2469,7 @@ SILC_SERVER_CMD_FUNC(invite)
     }
 
     /* Get the client entry */
-    dest = silc_server_get_client_resolve(server, dest_id, &resolve);
+    dest = silc_server_get_client_resolve(server, dest_id, FALSE, &resolve);
     if (!dest) {
       if (server->server_type != SILC_SERVER || !resolve) {
        silc_server_command_send_status_reply(
@@ -2479,14 +2491,15 @@ 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);
       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);
@@ -2582,7 +2595,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 ?
@@ -2590,7 +2603,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);
@@ -2644,7 +2657,7 @@ SILC_SERVER_CMD_FUNC(quit)
   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;
@@ -2656,8 +2669,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);
@@ -2703,7 +2716,7 @@ SILC_SERVER_CMD_FUNC(kill)
                                          SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
     goto out;
   }
-  client_id = silc_id_payload_parse_id(tmp, tmp_len);
+  client_id = silc_id_payload_parse_id(tmp, tmp_len, NULL);
   if (!client_id) {
     silc_server_command_send_status_reply(cmd, SILC_COMMAND_KILL,
                                          SILC_STATUS_ERR_NO_SUCH_CLIENT_ID);
@@ -2728,61 +2741,15 @@ 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 {
-    /* Update statistics */
-    if (remote_client->connection)
-      server->stat.my_clients--;
-    if (server->server_type == SILC_ROUTER)
-      server->stat.cell_clients--;
-    SILC_OPER_STATS_UPDATE(remote_client, server, SILC_UMODE_SERVER_OPERATOR);
-    SILC_OPER_STATS_UPDATE(remote_client, router, SILC_UMODE_ROUTER_OPERATOR);
-
-    /* Remove remote client */
-    silc_idlist_del_client(local ? server->local_list :
-                          server->global_list, remote_client);
-  }
+  /* Now do the killing */
+  silc_server_kill_client(server, remote_client, comment, client->id,
+                         SILC_ID_CLIENT);
 
  out:
   silc_server_command_free(cmd);
@@ -2812,7 +2779,7 @@ SILC_SERVER_CMD_FUNC(info)
   /* Get Server ID */
   tmp = silc_argument_get_arg_type(cmd->args, 2, &tmp_len);
   if (tmp) {
-    server_id = silc_id_payload_parse_id(tmp, tmp_len);
+    server_id = silc_id_payload_parse_id(tmp, tmp_len, NULL);
     if (!server_id) {
       silc_server_command_send_status_reply(cmd, SILC_COMMAND_INFO,
                                            SILC_STATUS_ERR_NO_SERVER_ID);
@@ -2932,7 +2899,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),
@@ -2988,6 +2955,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);
+    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);
+    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. */
@@ -3023,7 +3086,8 @@ 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, &resolve);
+    client = silc_server_get_client_resolve(server, client_id, FALSE, 
+                                           &resolve);
     if (!client) {
       if (cmd->pending)
        goto out;
@@ -3062,8 +3126,9 @@ static void silc_server_command_join_channel(SilcServer server,
       void *auth_data = (channel->founder_method == SILC_AUTH_PASSWORD ?
                         (void *)channel->founder_passwd : 
                         (void *)channel->founder_key);
-      SilcUInt32 auth_data_len = (channel->founder_method == SILC_AUTH_PASSWORD ?
-                             channel->founder_passwd_len : 0);
+      SilcUInt32 auth_data_len = 
+       (channel->founder_method == SILC_AUTH_PASSWORD ?
+        channel->founder_passwd_len : 0);
 
       /* Check whether the client is to become founder */
       if (silc_auth_verify_data(auth, auth_len, channel->founder_method, 
@@ -3157,7 +3222,7 @@ static void silc_server_command_join_channel(SilcServer server,
    */
 
   /* Check whether the client already is on the channel */
-  if (silc_server_client_on_channel(client, channel)) {
+  if (silc_server_client_on_channel(client, channel, NULL)) {
     silc_server_command_send_status_reply(cmd, SILC_COMMAND_JOIN,
                                          SILC_STATUS_ERR_USER_ON_CHANNEL);
     goto out;
@@ -3202,7 +3267,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,
@@ -3212,7 +3279,7 @@ static void silc_server_command_join_channel(SilcServer server,
 
   reply = 
     silc_command_reply_payload_encode_va(SILC_COMMAND_JOIN,
-                                        SILC_STATUS_OK, ident, 13,
+                                        SILC_STATUS_OK, 0, ident, 13,
                                         2, channel->channel_name,
                                         strlen(channel->channel_name),
                                         3, chidp->data, chidp->len,
@@ -3337,7 +3404,7 @@ SILC_SERVER_CMD_FUNC(join)
                                          SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
     goto out;
   }
-  client_id = silc_id_payload_parse_id(tmp, tmp_len);
+  client_id = silc_id_payload_parse_id(tmp, tmp_len, NULL);
   if (!client_id) {
     silc_server_command_send_status_reply(cmd, SILC_COMMAND_JOIN,
                                          SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
@@ -3467,8 +3534,7 @@ 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);
@@ -3534,13 +3600,15 @@ SILC_SERVER_CMD_FUNC(motd)
       
       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);
     }
 
@@ -3616,7 +3684,7 @@ SILC_SERVER_CMD_FUNC(motd)
 
     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 ? 
@@ -3641,81 +3709,60 @@ SILC_SERVER_CMD_FUNC(umode)
   SilcServer server = cmd->server;
   SilcClientEntry client = (SilcClientEntry)cmd->sock->user_data;
   SilcBuffer packet;
-  unsigned char *tmp_mask;
-  SilcUInt32 mask;
+  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);
       goto out;
     }
-  } else {
-    /* Remove the server operator rights */
-    if (client->mode & SILC_UMODE_SERVER_OPERATOR) {
-      client->mode &= ~SILC_UMODE_SERVER_OPERATOR;
-      if (client->connection)
-       server->stat.my_server_ops--;
-      if (server->server_type == SILC_ROUTER)
-       server->stat.server_ops--;
-    }
-  }
 
-  if (mask & SILC_UMODE_ROUTER_OPERATOR) {
-    if (!(client->mode & SILC_UMODE_ROUTER_OPERATOR)) {
-      /* Cannot operator mode */
-      silc_server_command_send_status_reply(cmd, SILC_COMMAND_UMODE,
-                                           SILC_STATUS_ERR_PERM_DENIED);
-      goto out;
-    }
-  } else {
-    /* Remove the router operator rights */
-    if (client->mode & SILC_UMODE_ROUTER_OPERATOR) {
-      client->mode &= ~SILC_UMODE_ROUTER_OPERATOR;
-      if (client->connection)
-       server->stat.my_router_ops--;
-      if (server->server_type == SILC_ROUTER)
-       server->stat.router_ops--;
+    /* 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);
+       goto out;
+      }
+    } else {
+      if (client->mode & SILC_UMODE_ANONYMOUS) {
+       silc_server_command_send_status_reply(cmd, SILC_COMMAND_UMODE,
+                                             SILC_STATUS_ERR_PERM_DENIED);
+       goto out;
+      }
     }
-  }
 
-  if (mask & SILC_UMODE_GONE) {
-    client->mode |= SILC_UMODE_GONE;
-  } else {
-    if (client->mode & SILC_UMODE_GONE)
-      /* Remove the gone status */
-      client->mode &= ~SILC_UMODE_GONE;
-  }
+    /* Change the mode */
+    client->mode = mask;
 
-  /* Send UMODE change to primary router */
-  if (!server->standalone)
-    silc_server_send_notify_umode(server, server->router->connection, TRUE,
-                                 client->id, client->mode);
+    /* Send UMODE change to primary router */
+    if (!server->standalone)
+      silc_server_send_notify_umode(server, server->router->connection, TRUE,
+                                   client->id, client->mode);
+  }
 
   /* 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);
@@ -3724,75 +3771,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,
-                                  SilcUInt32 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)
@@ -3801,16 +3779,17 @@ 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;
-  SilcUInt32 mode_mask, tmp_len, tmp_len2;
+  SilcUInt32 mode_mask = 0, tmp_len, tmp_len2;
   SilcUInt16 ident = silc_command_get_ident(cmd->payload);
+  bool set_mask = FALSE;
 
-  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);
@@ -3819,7 +3798,7 @@ SILC_SERVER_CMD_FUNC(cmode)
                                          SILC_STATUS_ERR_NO_CHANNEL_ID);
     goto out;
   }
-  channel_id = silc_id_payload_parse_id(tmp_id, tmp_len2);
+  channel_id = silc_id_payload_parse_id(tmp_id, tmp_len2, NULL);
   if (!channel_id) {
     silc_server_command_send_status_reply(cmd, SILC_COMMAND_CMODE,
                                          SILC_STATUS_ERR_NO_CHANNEL_ID);
@@ -3828,12 +3807,10 @@ SILC_SERVER_CMD_FUNC(cmode)
 
   /* 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, 
@@ -3849,19 +3826,34 @@ SILC_SERVER_CMD_FUNC(cmode)
   }
 
   /* Check whether this client is on the channel */
-  if (!silc_server_client_on_channel(client, channel)) {
+  if (!silc_server_client_on_channel(client, channel, &chl)) {
     silc_server_command_send_status_reply(cmd, SILC_COMMAND_CMODE,
                                          SILC_STATUS_ERR_NOT_ON_CHANNEL);
     goto out;
   }
 
-  /* Get entry to the channel user list */
-  silc_hash_table_find(channel->user_list, client, NULL, (void *)&chl);
-
   /* Check that client has rights to change any requested channel modes */
-  if (!silc_server_check_cmode_rights(channel, chl, mode_mask)) {
+  if (set_mask && !silc_server_check_cmode_rights(server, channel, chl, 
+                                                 mode_mask)) {
     silc_server_command_send_status_reply(cmd, SILC_COMMAND_CMODE,
-                                         SILC_STATUS_ERR_NO_CHANNEL_PRIV);
+                                         (chl->mode == 0 ? 
+                                          SILC_STATUS_ERR_NO_CHANNEL_PRIV :
+                                          SILC_STATUS_ERR_NO_CHANNEL_FOPRIV));
+    goto out;
+  }
+
+  /* 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;
   }
 
@@ -3930,15 +3922,13 @@ SILC_SERVER_CMD_FUNC(cmode)
       }
 
       /* 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;
     }
   }
 
@@ -4160,17 +4150,17 @@ SILC_SERVER_CMD_FUNC(cmode)
 
   /* 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(channel_id);
   silc_server_command_free(cmd);
 }
 
@@ -4202,7 +4192,7 @@ SILC_SERVER_CMD_FUNC(cumode)
                                          SILC_STATUS_ERR_NO_CHANNEL_ID);
     goto out;
   }
-  channel_id = silc_id_payload_parse_id(tmp_ch_id, tmp_ch_len);
+  channel_id = silc_id_payload_parse_id(tmp_ch_id, tmp_ch_len, NULL);
   if (!channel_id) {
     silc_server_command_send_status_reply(cmd, SILC_COMMAND_CUMODE,
                                          SILC_STATUS_ERR_NO_CHANNEL_ID);
@@ -4223,14 +4213,11 @@ SILC_SERVER_CMD_FUNC(cumode)
   }
 
   /* Check whether sender is on the channel */
-  if (!silc_server_client_on_channel(client, channel)) {
+  if (!silc_server_client_on_channel(client, channel, &chl)) {
     silc_server_command_send_status_reply(cmd, SILC_COMMAND_CUMODE,
                                          SILC_STATUS_ERR_NOT_ON_CHANNEL);
     goto out;
   }
-
-  /* Check that client has rights to change other's rights */
-  silc_hash_table_find(channel->user_list, client, NULL, (void *)&chl);
   sender_mask = chl->mode;
   
   /* Get the target client's channel mode mask */
@@ -4249,7 +4236,7 @@ SILC_SERVER_CMD_FUNC(cumode)
                                          SILC_STATUS_ERR_NO_CLIENT_ID);
     goto out;
   }
-  client_id = silc_id_payload_parse_id(tmp_id, tmp_len);
+  client_id = silc_id_payload_parse_id(tmp_id, tmp_len, NULL);
   if (!client_id) {
     silc_server_command_send_status_reply(cmd, SILC_COMMAND_CUMODE,
                                          SILC_STATUS_ERR_NO_CLIENT_ID);
@@ -4274,15 +4261,11 @@ SILC_SERVER_CMD_FUNC(cumode)
 
   /* Check whether target client is on the channel */
   if (target_client != client) {
-    if (!silc_server_client_on_channel(target_client, channel)) {
+    if (!silc_server_client_on_channel(target_client, channel, &chl)) {
       silc_server_command_send_status_reply(cmd, SILC_COMMAND_CUMODE,
                                 SILC_STATUS_ERR_USER_NOT_ON_CHANNEL);
       goto out;
     }
-
-    /* Get entry to the channel user list */
-    silc_hash_table_find(channel->user_list, target_client, NULL, 
-                        (void *)&chl);
   }
 
   /* 
@@ -4293,23 +4276,23 @@ SILC_SERVER_CMD_FUNC(cumode)
      but themselves. */
   if (chl->mode & SILC_CHANNEL_UMODE_CHANFO && client != target_client) {
     silc_server_command_send_status_reply(cmd, SILC_COMMAND_CUMODE,
-                                         SILC_STATUS_ERR_NO_CHANNEL_PRIV);
+                                         SILC_STATUS_ERR_NO_CHANNEL_FOPRIV);
     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);
+      goto out;
+    }
+
     if (!(chl->mode & SILC_CHANNEL_UMODE_CHANFO)) {
       /* The client tries to claim the founder rights. */
       unsigned char *tmp_auth;
       SilcUInt32 tmp_auth_len, auth_len;
       void *auth;
       
-      if (target_client != client) {
-       silc_server_command_send_status_reply(cmd, SILC_COMMAND_CUMODE,
-                                             SILC_STATUS_ERR_NOT_YOU);
-       goto out;
-      }
-
       if (!(channel->mode & SILC_CHANNEL_MODE_FOUNDER_AUTH) ||
          !channel->founder_key || !idata->public_key ||
          !silc_pkcs_public_key_compare(channel->founder_key, 
@@ -4384,6 +4367,31 @@ SILC_SERVER_CMD_FUNC(cumode)
     }
   }
 
+  if (target_mask & SILC_CHANNEL_UMODE_BLOCK_MESSAGES) {
+    if (target_client != client) {
+      silc_server_command_send_status_reply(cmd, SILC_COMMAND_CUMODE,
+                                           SILC_STATUS_ERR_NOT_YOU);
+      goto out;
+    }
+
+    if (!(chl->mode & SILC_CHANNEL_UMODE_BLOCK_MESSAGES)) {
+      chl->mode |= SILC_CHANNEL_UMODE_BLOCK_MESSAGES;
+      notify = TRUE;
+    }
+  } else {
+    if (chl->mode & SILC_CHANNEL_UMODE_BLOCK_MESSAGES) {
+      if (target_client != client) {
+       silc_server_command_send_status_reply(cmd, SILC_COMMAND_CUMODE,
+                                             SILC_STATUS_ERR_NOT_YOU);
+       goto out;
+      }
+
+      chl->mode &= ~SILC_CHANNEL_UMODE_BLOCK_MESSAGES;
+      notify = TRUE;
+    }
+  }
+
+
   idp = silc_id_payload_encode(client->id, SILC_ID_CLIENT);
   tmp_id = silc_argument_get_arg_type(cmd->args, 3, &tmp_len);
 
@@ -4407,7 +4415,7 @@ SILC_SERVER_CMD_FUNC(cumode)
 
   /* Send command reply to sender */
   packet = silc_command_reply_payload_encode_va(SILC_COMMAND_CUMODE,
-                                               SILC_STATUS_OK, ident, 3,
+                                               SILC_STATUS_OK, 0, ident, 3,
                                                2, tmp_mask, 4,
                                                3, tmp_ch_id, tmp_ch_len,
                                                4, tmp_id, tmp_len);
@@ -4448,7 +4456,7 @@ SILC_SERVER_CMD_FUNC(kick)
                                          SILC_STATUS_ERR_NO_CHANNEL_ID);
     goto out;
   }
-  channel_id = silc_id_payload_parse_id(tmp, tmp_len);
+  channel_id = silc_id_payload_parse_id(tmp, tmp_len, NULL);
   if (!channel_id) {
     silc_server_command_send_status_reply(cmd, SILC_COMMAND_KICK,
                                          SILC_STATUS_ERR_NO_CHANNEL_ID);
@@ -4469,14 +4477,13 @@ SILC_SERVER_CMD_FUNC(kick)
   }
 
   /* Check whether sender is on the channel */
-  if (!silc_server_client_on_channel(client, channel)) {
+  if (!silc_server_client_on_channel(client, channel, &chl)) {
     silc_server_command_send_status_reply(cmd, SILC_COMMAND_KICK,
                                          SILC_STATUS_ERR_NOT_ON_CHANNEL);
     goto out;
   }
 
   /* Check that the kicker is channel operator or channel founder */
-  silc_hash_table_find(channel->user_list, client, NULL, (void *)&chl);
   if (chl->mode == SILC_CHANNEL_UMODE_NONE) {
     silc_server_command_send_status_reply(cmd, SILC_COMMAND_KICK,
                                          SILC_STATUS_ERR_NO_CHANNEL_PRIV);
@@ -4490,7 +4497,7 @@ SILC_SERVER_CMD_FUNC(kick)
                                          SILC_STATUS_ERR_NO_CLIENT_ID);
     goto out;
   }
-  client_id = silc_id_payload_parse_id(target_idp, target_idp_len);
+  client_id = silc_id_payload_parse_id(target_idp, target_idp_len, NULL);
   if (!client_id) {
     silc_server_command_send_status_reply(cmd, SILC_COMMAND_KICK,
                                          SILC_STATUS_ERR_NO_CLIENT_ID);
@@ -4505,22 +4512,21 @@ SILC_SERVER_CMD_FUNC(kick)
                                                  client_id, TRUE, NULL);
   }
 
+  /* Check whether target client is on the channel */
+  if (!silc_server_client_on_channel(target_client, channel, &chl)) {
+    silc_server_command_send_status_reply(cmd, SILC_COMMAND_KICK,
+                                         SILC_STATUS_ERR_USER_NOT_ON_CHANNEL);
+    goto out;
+  }
+
   /* Check that the target client is not channel founder. Channel founder
      cannot be kicked from the channel. */
-  silc_hash_table_find(channel->user_list, target_client, NULL, (void *)&chl);
   if (chl->mode & SILC_CHANNEL_UMODE_CHANFO) {
     silc_server_command_send_status_reply(cmd, SILC_COMMAND_KICK,
                                          SILC_STATUS_ERR_NO_CHANNEL_FOPRIV);
     goto out;
   }
   
-  /* Check whether target client is on the channel */
-  if (!silc_server_client_on_channel(target_client, channel)) {
-    silc_server_command_send_status_reply(cmd, SILC_COMMAND_KICK,
-                                         SILC_STATUS_ERR_USER_NOT_ON_CHANNEL);
-    goto out;
-  }
-
   /* Get comment */
   tmp_len = 0;
   comment = silc_argument_get_arg_type(cmd->args, 3, &tmp_len);
@@ -4583,6 +4589,7 @@ SILC_SERVER_CMD_FUNC(oper)
   SilcServerConfigAdmin *admin;
   SilcIDListData idata = (SilcIDListData)client;
   bool result = FALSE;
+  SilcPublicKey cached_key;
 
   SILC_SERVER_COMMAND_CHECK(SILC_COMMAND_OPER, cmd, 1, 2);
 
@@ -4624,10 +4631,14 @@ SILC_SERVER_CMD_FUNC(oper)
     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->publickey)
+  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,
-                                  admin->publickey, 0,
-                                  idata->hash, client->id, SILC_ID_CLIENT);
+                                  cached_key, 0, idata->hash, 
+                                  client->id, SILC_ID_CLIENT);
+  }
   if (!result) {
     /* Authentication failed */
     silc_server_command_send_status_reply(cmd, SILC_COMMAND_OPER,
@@ -4657,6 +4668,61 @@ SILC_SERVER_CMD_FUNC(oper)
   silc_server_command_free(cmd);
 }
 
+SILC_TASK_CALLBACK(silc_server_command_detach_cb)
+{
+  QuitInternal q = (QuitInternal)context;
+  SilcClientEntry client = (SilcClientEntry)q->sock->user_data;
+
+  /* If there is pending outgoing data for the client then purge it
+     to the network before closing connection. */
+  silc_server_packet_queue_purge(q->server, q->sock);
+
+  /* Close the connection on our side */
+  client->router = NULL;
+  client->connection = NULL;
+  q->sock->user_data = NULL;
+  silc_server_close_connection(q->server, q->sock);
+
+  silc_free(q);
+}
+
+/* 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 (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;
+  if (!server->standalone)
+    silc_server_send_notify_umode(server, server->router->connection,
+                                 server->server_type == SILC_SERVER ?
+                                 FALSE : TRUE, client->id, client->mode);
+
+  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);
+
+  /* Send reply to the sender */
+  silc_server_command_send_status_reply(cmd, SILC_COMMAND_DETACH,
+                                       SILC_STATUS_OK);
+
+ out:
+  silc_server_command_free(cmd);
+}
+
 /* Server side of SILCOPER command. Client uses this comand to obtain router
    operator privileges to this router. */
 
@@ -4670,6 +4736,7 @@ SILC_SERVER_CMD_FUNC(silcoper)
   SilcServerConfigAdmin *admin;
   SilcIDListData idata = (SilcIDListData)client;
   bool result = FALSE;
+  SilcPublicKey cached_key;
 
   SILC_SERVER_COMMAND_CHECK(SILC_COMMAND_SILCOPER, cmd, 1, 2);
 
@@ -4717,10 +4784,14 @@ SILC_SERVER_CMD_FUNC(silcoper)
     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->publickey)
+  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,
-                                  admin->publickey, 0,
-                                  idata->hash, client->id, SILC_ID_CLIENT);
+                                  cached_key, 0, idata->hash, 
+                                  client->id, SILC_ID_CLIENT);
+  }
   if (!result) {
     /* Authentication failed */
     silc_server_command_send_status_reply(cmd, SILC_COMMAND_OPER,
@@ -4750,65 +4821,10 @@ SILC_SERVER_CMD_FUNC(silcoper)
   silc_server_command_free(cmd);
 }
 
-/* Server side command of CONNECT. Connects us to the specified remote
-   server or router. */
+/* Server side of command BAN. This is used to manage the ban list of the
+   channel. To add clients and remove clients from the ban list. */
 
-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_CONNECT, cmd, 1, 2);
-
-  if (!client || cmd->sock->type != SILC_SOCKET_TYPE_CLIENT)
-    goto out;
-
-  /* Check whether client has the permissions. */
-  if (client->mode == SILC_UMODE_NONE) {
-    silc_server_command_send_status_reply(cmd, SILC_COMMAND_CONNECT,
-                                         SILC_STATUS_ERR_NO_SERVER_PRIV);
-    goto out;
-  }
-
-  if (server->server_type == SILC_ROUTER && 
-      client->mode & SILC_UMODE_SERVER_OPERATOR) {
-    silc_server_command_send_status_reply(cmd, SILC_COMMAND_CONNECT,
-                                         SILC_STATUS_ERR_NO_ROUTER_PRIV);
-    goto out;
-  }
-
-  /* Get the remote server */
-  host = silc_argument_get_arg_type(cmd->args, 1, &tmp_len);
-  if (!host) {
-    silc_server_command_send_status_reply(cmd, SILC_COMMAND_CONNECT,
-                                         SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
-    goto out;
-  }
-
-  /* Get port */
-  tmp = silc_argument_get_arg_type(cmd->args, 2, &tmp_len);
-  if (tmp)
-    SILC_GET32_MSB(port, tmp);
-
-  /* Create the connection. It is done with timeout and is async. */
-  silc_server_create_connection(server, host, port);
-
-  /* Send reply to the sender */
-  silc_server_command_send_status_reply(cmd, SILC_COMMAND_CONNECT,
-                                       SILC_STATUS_OK);
-
- out:
-  silc_server_command_free(cmd);
-}
-
-/* Server side of command BAN. This is used to manage the ban list of the
-   channel. To add clients and remove clients from the ban list. */
-
-SILC_SERVER_CMD_FUNC(ban)
+SILC_SERVER_CMD_FUNC(ban)
 {
   SilcServerCommandContext cmd = (SilcServerCommandContext)context;
   SilcServer server = cmd->server;
@@ -4829,7 +4845,7 @@ SILC_SERVER_CMD_FUNC(ban)
   /* Get Channel ID */
   id = silc_argument_get_arg_type(cmd->args, 1, &id_len);
   if (id) {
-    channel_id = silc_id_payload_parse_id(id, id_len);
+    channel_id = silc_id_payload_parse_id(id, id_len, NULL);
     if (!channel_id) {
       silc_server_command_send_status_reply(cmd, SILC_COMMAND_BAN,
                                            SILC_STATUS_ERR_NO_CHANNEL_ID);
@@ -4852,14 +4868,7 @@ SILC_SERVER_CMD_FUNC(ban)
   }
 
   /* Check whether this client is on the channel */
-  if (!silc_server_client_on_channel(client, channel)) {
-    silc_server_command_send_status_reply(cmd, SILC_COMMAND_BAN,
-                                         SILC_STATUS_ERR_NOT_ON_CHANNEL);
-    goto out;
-  }
-
-  /* Get entry to the channel user list */
-  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_BAN,
                                          SILC_STATUS_ERR_NOT_ON_CHANNEL);
     goto out;
@@ -4920,7 +4929,7 @@ 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 ? 
@@ -4935,111 +4944,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;
-  SilcUInt32 tmp_len;
-  unsigned char *name;
-  SilcUInt32 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)
@@ -5062,7 +4966,7 @@ SILC_SERVER_CMD_FUNC(leave)
                                          SILC_STATUS_ERR_NO_CHANNEL_ID);
     goto out;
   }
-  id = silc_id_payload_parse_id(tmp, len);
+  id = silc_id_payload_parse_id(tmp, len, NULL);
   if (!id) {
     silc_server_command_send_status_reply(cmd, SILC_COMMAND_LEAVE,
                                          SILC_STATUS_ERR_NO_CHANNEL_ID);
@@ -5081,7 +4985,7 @@ SILC_SERVER_CMD_FUNC(leave)
   }
 
   /* Check whether this client is on the channel */
-  if (!silc_server_client_on_channel(id_entry, channel)) {
+  if (!silc_server_client_on_channel(id_entry, channel, NULL)) {
     silc_server_command_send_status_reply(cmd, SILC_COMMAND_LEAVE,
                                          SILC_STATUS_ERR_NOT_ON_CHANNEL);
     goto out;
@@ -5094,8 +4998,8 @@ SILC_SERVER_CMD_FUNC(leave)
                                  server->server_type == SILC_ROUTER ?
                                  TRUE : FALSE, channel, id_entry->id);
 
-  silc_server_command_send_status_reply(cmd, SILC_COMMAND_LEAVE,
-                                       SILC_STATUS_OK);
+  silc_server_command_send_status_data(cmd, SILC_COMMAND_LEAVE,
+                                      SILC_STATUS_OK, 2, tmp, len);
 
   /* Remove client from channel */
   if (!silc_server_remove_from_one_channel(server, sock, channel, id_entry,
@@ -5154,7 +5058,7 @@ SILC_SERVER_CMD_FUNC(users)
   }
 
   if (channel_id) {
-    id = silc_id_payload_parse_id(channel_id, channel_id_len);
+    id = silc_id_payload_parse_id(channel_id, channel_id_len, NULL);
     if (!id) {
       silc_server_command_send_status_reply(cmd, SILC_COMMAND_USERS,
                                            SILC_STATUS_ERR_NO_CHANNEL_ID);
@@ -5171,7 +5075,7 @@ SILC_SERVER_CMD_FUNC(users)
     channel = silc_idlist_find_channel_by_name(server->local_list, 
                                               channel_name, NULL);
 
-  if (!channel || channel->disabled) {
+  if (!channel || channel->disabled || !channel->users_resolved) {
     if (server->server_type != SILC_ROUTER && !server->standalone &&
        !cmd->pending) {
       SilcBuffer tmpbuf;
@@ -5211,19 +5115,13 @@ SILC_SERVER_CMD_FUNC(users)
   }
 
   /* If the channel is private or secret do not send anything, unless the
-     user requesting this command is on the channel. */
+     user requesting this command is on the channel or is server */
   if (cmd->sock->type == SILC_SOCKET_TYPE_CLIENT) {
     if (channel->mode & (SILC_CHANNEL_MODE_PRIVATE | SILC_CHANNEL_MODE_SECRET)
-       && !silc_server_client_on_channel(cmd->sock->user_data, channel)) {
-      silc_server_command_send_status_reply(cmd, SILC_COMMAND_USERS,
-                                           SILC_STATUS_ERR_NO_SUCH_CHANNEL);
-      goto out;
-    }
-  } else {
-    if (channel->mode & 
-       (SILC_CHANNEL_MODE_PRIVATE | SILC_CHANNEL_MODE_SECRET)) {
+       && !silc_server_client_on_channel(cmd->sock->user_data, channel, 
+                                         NULL)) {
       silc_server_command_send_status_reply(cmd, SILC_COMMAND_USERS,
-                                           SILC_STATUS_ERR_NO_SUCH_CHANNEL);
+                                           SILC_STATUS_ERR_NOT_ON_CHANNEL);
       goto out;
     }
   }
@@ -5238,7 +5136,7 @@ SILC_SERVER_CMD_FUNC(users)
   /* Send reply */
   idp = silc_id_payload_encode(channel->id, SILC_ID_CHANNEL);
   packet = silc_command_reply_payload_encode_va(SILC_COMMAND_USERS,
-                                               SILC_STATUS_OK, ident, 4,
+                                               SILC_STATUS_OK, 0, ident, 4,
                                                2, idp->data, idp->len,
                                                3, lc, 4,
                                                4, client_id_list->data,
@@ -5306,14 +5204,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;
       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;
       
@@ -5433,7 +5332,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);
@@ -5451,3 +5350,166 @@ SILC_SERVER_CMD_FUNC(getkey)
   silc_free(server_id);
   silc_server_command_free(cmd);
 }
+
+
+/* Private range commands, specific to this implementation */
+
+/* Server side command of CONNECT. Connects us to the specified remote
+   server or router. */
+
+SILC_SERVER_CMD_FUNC(connect)
+{
+  SilcServerCommandContext cmd = (SilcServerCommandContext)context;
+  SilcServer server = cmd->server;
+  SilcClientEntry client = (SilcClientEntry)cmd->sock->user_data;
+  unsigned char *tmp, *host;
+  SilcUInt32 tmp_len;
+  SilcUInt32 port = SILC_PORT;
+
+  SILC_SERVER_COMMAND_CHECK(SILC_COMMAND_PRIV_CONNECT, cmd, 1, 2);
+
+  if (!client || cmd->sock->type != SILC_SOCKET_TYPE_CLIENT)
+    goto out;
+
+  /* Check whether client has the permissions. */
+  if (client->mode == SILC_UMODE_NONE) {
+    silc_server_command_send_status_reply(cmd, SILC_COMMAND_PRIV_CONNECT,
+                                         SILC_STATUS_ERR_NO_SERVER_PRIV);
+    goto out;
+  }
+
+  if (server->server_type == SILC_ROUTER && 
+      client->mode & SILC_UMODE_SERVER_OPERATOR) {
+    silc_server_command_send_status_reply(cmd, SILC_COMMAND_PRIV_CONNECT,
+                                         SILC_STATUS_ERR_NO_ROUTER_PRIV);
+    goto out;
+  }
+
+  /* Get the remote server */
+  host = silc_argument_get_arg_type(cmd->args, 1, &tmp_len);
+  if (!host) {
+    silc_server_command_send_status_reply(cmd, SILC_COMMAND_PRIV_CONNECT,
+                                         SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
+    goto out;
+  }
+
+  /* Get port */
+  tmp = silc_argument_get_arg_type(cmd->args, 2, &tmp_len);
+  if (tmp)
+    SILC_GET32_MSB(port, tmp);
+
+  /* Create the connection. It is done with timeout and is async. */
+  silc_server_create_connection(server, host, port);
+
+  /* Send reply to the sender */
+  silc_server_command_send_status_reply(cmd, SILC_COMMAND_PRIV_CONNECT,
+                                       SILC_STATUS_OK);
+
+ out:
+  silc_server_command_free(cmd);
+}
+
+/* Server side command of CLOSE. Closes connection to a specified server. */
+SILC_SERVER_CMD_FUNC(close)
+{
+  SilcServerCommandContext cmd = (SilcServerCommandContext)context;
+  SilcServer server = cmd->server;
+  SilcClientEntry client = (SilcClientEntry)cmd->sock->user_data;
+  SilcServerEntry server_entry;
+  SilcSocketConnection sock;
+  unsigned char *tmp;
+  SilcUInt32 tmp_len;
+  unsigned char *name;
+  SilcUInt32 port = SILC_PORT;
+
+  SILC_SERVER_COMMAND_CHECK(SILC_COMMAND_PRIV_CLOSE, cmd, 1, 2);
+
+  if (!client || cmd->sock->type != SILC_SOCKET_TYPE_CLIENT)
+    goto out;
+
+  /* Check whether client has the permissions. */
+  if (client->mode == SILC_UMODE_NONE) {
+    silc_server_command_send_status_reply(cmd, SILC_COMMAND_PRIV_CLOSE,
+                                         SILC_STATUS_ERR_NO_SERVER_PRIV);
+    goto out;
+  }
+
+  /* Get the remote server */
+  name = silc_argument_get_arg_type(cmd->args, 1, &tmp_len);
+  if (!name) {
+    silc_server_command_send_status_reply(cmd, SILC_COMMAND_PRIV_CLOSE,
+                                         SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
+    goto out;
+  }
+
+  /* Get port */
+  tmp = silc_argument_get_arg_type(cmd->args, 2, &tmp_len);
+  if (tmp)
+    SILC_GET32_MSB(port, tmp);
+
+  server_entry = silc_idlist_find_server_by_conn(server->local_list,
+                                                name, port, FALSE, NULL);
+  if (!server_entry)
+    server_entry = silc_idlist_find_server_by_conn(server->global_list,
+                                                  name, port, FALSE, NULL);
+  if (!server_entry) {
+    silc_server_command_send_status_reply(cmd, SILC_COMMAND_PRIV_CLOSE,
+                                         SILC_STATUS_ERR_NO_SERVER_ID);
+    goto out;
+  }
+
+  /* Send reply to the sender */
+  silc_server_command_send_status_reply(cmd, SILC_COMMAND_PRIV_CLOSE,
+                                       SILC_STATUS_OK);
+
+  /* Close the connection to the server */
+  sock = (SilcSocketConnection)server_entry->connection;
+
+  /* If we shutdown primary router connection manually then don't trigger
+     any reconnect or backup router connections, by setting the router
+     to NULL here. */
+  if (server->router == server_entry) {
+    server->id_entry->router = NULL;
+    server->router = NULL;
+    server->standalone = TRUE;
+  }
+  silc_server_free_sock_user_data(server, sock, NULL);
+  silc_server_close_connection(server, sock);
+  
+ out:
+  silc_server_command_free(cmd);
+}
+
+/* Server side command of SHUTDOWN. Shutdowns the server and closes all
+   active connections. */
+SILC_SERVER_CMD_FUNC(shutdown)
+{
+  SilcServerCommandContext cmd = (SilcServerCommandContext)context;
+  SilcServer server = cmd->server;
+  SilcClientEntry client = (SilcClientEntry)cmd->sock->user_data;
+
+  SILC_SERVER_COMMAND_CHECK(SILC_COMMAND_PRIV_SHUTDOWN, cmd, 0, 0);
+
+  if (!client || cmd->sock->type != SILC_SOCKET_TYPE_CLIENT)
+    goto out;
+
+  /* Check whether client has the permission. */
+  if (client->mode == SILC_UMODE_NONE) {
+    silc_server_command_send_status_reply(cmd, SILC_COMMAND_PRIV_SHUTDOWN,
+                                         SILC_STATUS_ERR_NO_SERVER_PRIV);
+    goto out;
+  }
+
+  /* Send reply to the sender */
+  silc_server_command_send_status_reply(cmd, SILC_COMMAND_PRIV_SHUTDOWN,
+                                       SILC_STATUS_OK);
+
+  /* Then, gracefully, or not, bring the server down. */
+  silc_server_stop(server);
+  exit(0);
+
+ out:
+  silc_server_command_free(cmd);
+}