updates.
[silc.git] / apps / silcd / command.c
index 87eebd88d208b0c19f1714db6ab7c62d226ed6d7..146c96a7efbcd1731217d5f3b16dbc15fedc5c62 100644 (file)
@@ -4,7 +4,7 @@
 
   Author: Pekka Riikonen <priikone@poseidon.pspt.fi>
 
-  Copyright (C) 1997 - 2000 Pekka Riikonen
+  Copyright (C) 1997 - 2001 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
@@ -37,10 +37,7 @@ silc_server_command_send_status_data(SilcServerCommandContext cmd,
                                     unsigned int arg_type,
                                     unsigned char *arg,
                                     unsigned int arg_len);
-static void silc_server_command_free(SilcServerCommandContext cmd);
-void silc_server_command_send_names(SilcServer server,
-                                   SilcSocketConnection sock,
-                                   SilcChannelEntry channel);
+SILC_TASK_CALLBACK(silc_server_command_process_timeout);
 
 /* Server command list. */
 SilcServerCommand silc_command_list[] =
@@ -48,32 +45,33 @@ SilcServerCommand silc_command_list[] =
   SILC_SERVER_CMD(whois, WHOIS, SILC_CF_LAG | SILC_CF_REG),
   SILC_SERVER_CMD(whowas, WHOWAS, SILC_CF_LAG | SILC_CF_REG),
   SILC_SERVER_CMD(identify, IDENTIFY, SILC_CF_LAG | SILC_CF_REG),
-  SILC_SERVER_CMD(nick, NICK, SILC_CF_LAG | SILC_CF_REG),
-  SILC_SERVER_CMD(list, LIST, SILC_CF_LAG | SILC_CF_REG),
+  SILC_SERVER_CMD(nick, NICK, SILC_CF_LAG_STRICT | SILC_CF_REG),
+  SILC_SERVER_CMD(list, LIST, SILC_CF_LAG_STRICT | SILC_CF_REG),
   SILC_SERVER_CMD(topic, TOPIC, SILC_CF_LAG | SILC_CF_REG),
   SILC_SERVER_CMD(invite, INVITE, SILC_CF_LAG | SILC_CF_REG),
   SILC_SERVER_CMD(quit, QUIT, SILC_CF_LAG | SILC_CF_REG),
-  SILC_SERVER_CMD(kill, KILL, SILC_CF_LAG | SILC_CF_REG | SILC_CF_OPER),
+  SILC_SERVER_CMD(kill, KILL, SILC_CF_LAG_STRICT | SILC_CF_REG | SILC_CF_OPER),
   SILC_SERVER_CMD(info, INFO, SILC_CF_LAG | SILC_CF_REG),
   SILC_SERVER_CMD(connect, CONNECT, 
                  SILC_CF_LAG | SILC_CF_REG | SILC_CF_OPER),
   SILC_SERVER_CMD(ping, PING, SILC_CF_LAG | SILC_CF_REG),
   SILC_SERVER_CMD(oper, OPER, SILC_CF_LAG | SILC_CF_REG | SILC_CF_OPER),
-  SILC_SERVER_CMD(join, JOIN, SILC_CF_LAG | SILC_CF_REG),
+  SILC_SERVER_CMD(join, JOIN, SILC_CF_LAG_STRICT | SILC_CF_REG),
   SILC_SERVER_CMD(motd, MOTD, SILC_CF_LAG | SILC_CF_REG),
   SILC_SERVER_CMD(umode, UMODE, SILC_CF_LAG | SILC_CF_REG),
-  SILC_SERVER_CMD(cmode, CMODE, SILC_CF_LAG | SILC_CF_REG),
+  SILC_SERVER_CMD(cmode, CMODE, SILC_CF_LAG_STRICT | SILC_CF_REG),
   SILC_SERVER_CMD(cumode, CUMODE, SILC_CF_LAG | SILC_CF_REG),
-  SILC_SERVER_CMD(kick, KICK, SILC_CF_LAG | SILC_CF_REG),
+  SILC_SERVER_CMD(kick, KICK, SILC_CF_LAG_STRICT | SILC_CF_REG),
   SILC_SERVER_CMD(restart, RESTART, 
                  SILC_CF_LAG | SILC_CF_REG | SILC_CF_OPER),
   SILC_SERVER_CMD(close, CLOSE,
                  SILC_CF_LAG | SILC_CF_REG | SILC_CF_OPER),
-  SILC_SERVER_CMD(die, DIE, SILC_CF_LAG | SILC_CF_REG | SILC_CF_OPER),
+  SILC_SERVER_CMD(shutdown, SHUTDOWN, SILC_CF_LAG | SILC_CF_REG | 
+                 SILC_CF_OPER),
   SILC_SERVER_CMD(silcoper, SILCOPER,
                  SILC_CF_LAG | SILC_CF_REG | SILC_CF_SILC_OPER),
-  SILC_SERVER_CMD(leave, LEAVE, SILC_CF_LAG | SILC_CF_REG),
-  SILC_SERVER_CMD(names, NAMES, SILC_CF_LAG | SILC_CF_REG),
+  SILC_SERVER_CMD(leave, LEAVE, SILC_CF_LAG_STRICT | SILC_CF_REG),
+  SILC_SERVER_CMD(users, USERS, SILC_CF_LAG | SILC_CF_REG),
 
   { NULL, 0 },
 };
@@ -116,6 +114,34 @@ static int silc_server_is_registered(SilcServer server,
   return FALSE;
 }
 
+/* Internal context to hold data when executed command with timeout. */
+typedef struct {
+  SilcServerCommandContext ctx;
+  SilcServerCommand *cmd;
+} *SilcServerCommandTimeout;
+
+/* Timeout callback to process commands with timeout for client. Client's
+   commands are always executed with timeout. */
+
+SILC_TASK_CALLBACK(silc_server_command_process_timeout)
+{
+  SilcServerCommandTimeout timeout = (SilcServerCommandTimeout)context;
+  SilcClientEntry client = (SilcClientEntry)timeout->ctx->sock->user_data;
+
+  /* Update access time */
+  client->last_command = time(NULL);
+
+  if (!(timeout->cmd->flags & SILC_CF_REG))
+    timeout->cmd->cb(timeout->ctx);
+  else if (silc_server_is_registered(timeout->ctx->server, 
+                                    timeout->ctx->sock, 
+                                    timeout->ctx, 
+                                    timeout->cmd->cmd))
+    timeout->cmd->cb(timeout->ctx);
+
+  silc_free(timeout);
+}
+
 /* Processes received command packet. */
 
 void silc_server_command_process(SilcServer server,
@@ -124,35 +150,13 @@ void silc_server_command_process(SilcServer server,
 {
   SilcServerCommandContext ctx;
   SilcServerCommand *cmd;
+  SilcCommand command;
 
-#if 0
-  /* XXX allow commands in but do not execute them more than once per
-     two seconds. */
-
-  /* Check whether it is allowed for this connection to execute any
-     command. */
-  if (sock->type == SILC_SOCKET_TYPE_CLIENT) {
-    time_t curtime;
-    SilcClientEntry client = (SilcClientEntry)sock->user_data;
-
-    if (!client)
-      return;
-
-    /* Allow only one command executed in 2 seconds. */
-    curtime = time(NULL);
-    if (client->last_command && (curtime - client->last_command) < 2)
-      return;
-
-    /* Update access time */
-    client->last_command = curtime;
-  }
-#endif
-  
   /* Allocate command context. This must be free'd by the
      command routine receiving it. */
-  ctx = silc_calloc(1, sizeof(*ctx));
+  ctx = silc_server_command_alloc();
   ctx->server = server;
-  ctx->sock = sock;
+  ctx->sock = silc_socket_dup(sock);
   ctx->packet = silc_packet_context_dup(packet); /* Save original packet */
   
   /* Parse the command payload in the packet */
@@ -161,31 +165,109 @@ void silc_server_command_process(SilcServer server,
     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);
     return;
   }
   ctx->args = silc_command_get_args(ctx->payload);
-  
-  /* Execute command. If this fails the packet is dropped. */
-  for (cmd = silc_command_list; cmd->cb; cmd++)
-    if (cmd->cmd == silc_command_get(ctx->payload)) {
 
-      if (!(cmd->flags & SILC_CF_REG)) {
-       cmd->cb(ctx);
-       break;
-      }
-      
-      if (silc_server_is_registered(server, sock, ctx, cmd->cmd)) {
-       cmd->cb(ctx);
-       break;
-      }
-    }
+  /* Get the command */
+  command = silc_command_get(ctx->payload);
+  for (cmd = silc_command_list; cmd->cb; cmd++)
+    if (cmd->cmd == command)
+      break;
 
   if (cmd == NULL) {
-    SILC_LOG_ERROR(("Unknown command, packet dropped"));
+    silc_server_command_send_status_reply(ctx, command,
+                                         SILC_STATUS_ERR_UNKNOWN_COMMAND);
     silc_server_command_free(ctx);
     return;
   }
+
+  /* Execute client's commands always with timeout.  Normally they are
+     executed with zero (0) timeout but if client is sending command more
+     frequently than once in 2 seconds, then the timeout may be 0 to 2
+     seconds. */
+  if (sock->type == SILC_SOCKET_TYPE_CLIENT) {
+    SilcClientEntry client = (SilcClientEntry)sock->user_data;
+    SilcServerCommandTimeout timeout = silc_calloc(1, sizeof(*timeout));
+    int fast;
+
+    timeout->ctx = ctx;
+    timeout->cmd = cmd;
+
+    if (client->last_command && (time(NULL) - client->last_command) < 2) {
+      client->fast_command++;
+      fast = FALSE;
+    } else {
+      client->fast_command = ((client->fast_command - 1) <= 0 ? 0 : 
+                             client->fast_command--);
+      fast = TRUE;
+    }
+
+    if (!fast && ((cmd->flags & SILC_CF_LAG_STRICT) ||
+                 (client->fast_command > 5 && cmd->flags & SILC_CF_LAG)))
+      silc_task_register(server->timeout_queue, sock->sock, 
+                        silc_server_command_process_timeout,
+                        (void *)timeout, 
+                        2 - (time(NULL) - client->last_command), 0,
+                        SILC_TASK_TIMEOUT,
+                        SILC_TASK_PRI_NORMAL);
+    else
+      silc_task_register(server->timeout_queue, sock->sock, 
+                        silc_server_command_process_timeout,
+                        (void *)timeout, 
+                        0, 1,
+                        SILC_TASK_TIMEOUT,
+                        SILC_TASK_PRI_NORMAL);
+    return;
+  }
+
+  /* Execute for server */
+
+  if (!(cmd->flags & SILC_CF_REG))
+    cmd->cb(ctx);
+  else if (silc_server_is_registered(server, sock, ctx, cmd->cmd))
+    cmd->cb(ctx);
+}
+
+/* Allocate Command Context */
+
+SilcServerCommandContext silc_server_command_alloc()
+{
+  SilcServerCommandContext ctx = silc_calloc(1, sizeof(*ctx));
+  ctx->users++;
+  return ctx;
+}
+
+/* Free's the command context allocated before executing the command */
+
+void silc_server_command_free(SilcServerCommandContext ctx)
+{
+  ctx->users--;
+  SILC_LOG_DEBUG(("Command context %p refcnt %d->%d", ctx, ctx->users + 1,
+                 ctx->users));
+  if (ctx->users < 1) {
+    if (ctx->payload)
+      silc_command_free_payload(ctx->payload);
+    if (ctx->packet)
+      silc_packet_context_free(ctx->packet);
+    if (ctx->sock)
+      silc_socket_free(ctx->sock); /* Decrease reference counter */
+    silc_free(ctx);
+  }
+}
+
+/* Duplicate Command Context by adding reference counter. The context won't
+   be free'd untill it hits zero. */
+
+SilcServerCommandContext 
+silc_server_command_dup(SilcServerCommandContext ctx)
+{
+  ctx->users++;
+  SILC_LOG_DEBUG(("Command context %p refcnt %d->%d", ctx, ctx->users - 1,
+                 ctx->users));
+  return ctx;
 }
 
 /* Add new pending command to be executed when reply to a command has been
@@ -197,6 +279,7 @@ void silc_server_command_process(SilcServer server,
 void silc_server_command_pending(SilcServer server,
                                 SilcCommand reply_cmd,
                                 unsigned short ident,
+                                SilcServerPendingDestructor destructor,
                                 SilcCommandCb callback,
                                 void *context)
 {
@@ -207,6 +290,7 @@ void silc_server_command_pending(SilcServer server,
   reply->ident = ident;
   reply->context = context;
   reply->callback = callback;
+  reply->destructor = destructor;
   silc_dlist_add(server->pending_commands, reply);
 }
 
@@ -242,6 +326,7 @@ int silc_server_command_pending_check(SilcServer server,
     if (r->reply_cmd == command && r->ident == ident) {
       ctx->context = r->context;
       ctx->callback = r->callback;
+      ctx->destructor = r->destructor;
       ctx->ident = ident;
       return TRUE;
     }
@@ -250,17 +335,12 @@ int silc_server_command_pending_check(SilcServer server,
   return FALSE;
 }
 
-/* Free's the command context allocated before executing the command */
+/* Destructor function for pending callbacks. This is called when using
+   pending commands to free the context given for the pending command. */
 
-static void silc_server_command_free(SilcServerCommandContext cmd)
+static void silc_server_command_destructor(void *context)
 {
-  if (cmd) {
-    if (cmd->payload)
-      silc_command_free_payload(cmd->payload);
-    if (cmd->packet)
-      silc_packet_context_free(cmd->packet);
-    silc_free(cmd);
-  }
+  silc_server_command_free((SilcServerCommandContext)context);
 }
 
 /* Sends simple status message as command reply packet */
@@ -274,7 +354,10 @@ 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, 0, 0);
+  buffer = 
+    silc_command_reply_payload_encode_va(command, status, 
+                                        silc_command_get_ident(cmd->payload),
+                                        0);
   silc_server_packet_send(cmd->server, cmd->sock,
                          SILC_PACKET_COMMAND_REPLY, 0, 
                          buffer->data, buffer->len, FALSE);
@@ -296,8 +379,10 @@ 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, 0, 1,
-                                               arg_type, arg, arg_len);
+  buffer = 
+    silc_command_reply_payload_encode_va(command, status, 
+                                        silc_command_get_ident(cmd->payload),
+                                        1, arg_type, arg, arg_len);
   silc_server_packet_send(cmd->server, cmd->sock,
                          SILC_PACKET_COMMAND_REPLY, 0, 
                          buffer->data, buffer->len, FALSE);
@@ -312,17 +397,20 @@ silc_server_command_send_status_data(SilcServerCommandContext cmd,
 
 static int
 silc_server_command_whois_parse(SilcServerCommandContext cmd,
-                               SilcClientID **client_id,
+                               SilcClientID ***client_id,
+                               unsigned int *client_id_count,
                                char **nickname,
                                char **server_name,
-                               int *count)
+                               int *count,
+                               SilcCommand command)
 {
   unsigned char *tmp;
   unsigned int len;
   unsigned int argc = silc_argument_get_arg_num(cmd->args);
+  int i, k;
 
   /* If client ID is in the command it must be used instead of nickname */
-  tmp = silc_argument_get_arg_type(cmd->args, 2, &len);
+  tmp = silc_argument_get_arg_type(cmd->args, 3, &len);
   if (!tmp) {
     /* No ID, get the nickname@server string and parse it. */
     tmp = silc_argument_get_arg_type(cmd->args, 1, NULL);
@@ -337,30 +425,52 @@ silc_server_command_whois_parse(SilcServerCommandContext cmd,
        *nickname = strdup(tmp);
       }
     } else {
-      silc_server_command_send_status_reply(cmd, SILC_COMMAND_WHOIS,
+      silc_server_command_send_status_reply(cmd, command,
                                            SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
       return FALSE;
     }
   } else {
+    /* Command includes ID, we must use that.  Also check whether the command
+       has more than one ID set - take them all. */
+
+    *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, use that */
-    *client_id = silc_id_payload_parse_id(tmp, len);
   }
 
   /* Get the max count of reply messages allowed */
-  if (argc == 3) {
-    tmp = silc_argument_get_arg_type(cmd->args, 3, NULL);
-    if (!tmp) {
-      silc_server_command_send_status_reply(cmd, SILC_COMMAND_WHOIS,
-                                           SILC_STATUS_ERR_TOO_MANY_PARAMS);
-      if (*nickname)
-       silc_free(*nickname);
-      if (*server_name)
-       silc_free(*server_name);
-
-      return FALSE;
-    }
+  tmp = silc_argument_get_arg_type(cmd->args, 2, NULL);
+  if (tmp)
     *count = atoi(tmp);
-  }
+  else
+    *count = 0;
 
   return TRUE;
 }
@@ -380,6 +490,9 @@ silc_server_command_whois_check(SilcServerCommandContext cmd,
     if (!entry->nickname || !entry->username || !entry->userinfo) {
       SilcBuffer tmpbuf;
       unsigned short old_ident;
+
+      if (!entry->router)
+       continue;
       
       old_ident = silc_command_get_ident(cmd->payload);
       silc_command_set_ident(cmd->payload, silc_rng_get_rn16(server->rng));
@@ -393,7 +506,9 @@ silc_server_command_whois_check(SilcServerCommandContext cmd,
       /* Reprocess this packet after received reply */
       silc_server_command_pending(server, SILC_COMMAND_WHOIS, 
                                  silc_command_get_ident(cmd->payload),
-                                 silc_server_command_whois, (void *)cmd);
+                                 silc_server_command_destructor,
+                                 silc_server_command_whois, 
+                                 silc_server_command_dup(cmd));
       cmd->pending = TRUE;
       
       silc_command_set_ident(cmd->payload, old_ident);
@@ -414,10 +529,13 @@ silc_server_command_whois_send_reply(SilcServerCommandContext cmd,
   SilcServer server = cmd->server;
   char *tmp;
   int i, count = 0, len;
-  SilcBuffer packet, idp;
+  SilcBuffer packet, idp, channels;
   SilcClientEntry entry;
   SilcCommandStatus status;
   unsigned short ident = silc_command_get_ident(cmd->payload);
+  char nh[128], uh[128];
+  unsigned char idle[4], mode[4];
+  SilcSocketConnection hsock;
 
   status = SILC_STATUS_OK;
   if (clients_count > 1)
@@ -426,6 +544,15 @@ silc_server_command_whois_send_reply(SilcServerCommandContext cmd,
   for (i = 0; i < clients_count; i++) {
     entry = clients[i];
 
+    if (entry->connection && entry->data.registered == FALSE) {
+      if (clients_count == 1)
+       silc_server_command_send_status_data(cmd, SILC_COMMAND_WHOIS,
+                                            SILC_STATUS_ERR_NO_SUCH_NICK,
+                                            3, entry->nickname, 
+                                            strlen(entry->nickname));
+      continue;
+    }
+
     if (count && i - 1 == count)
       break;
 
@@ -435,64 +562,74 @@ silc_server_command_whois_send_reply(SilcServerCommandContext cmd,
     if (clients_count > 1 && i == clients_count - 1)
       status = SILC_STATUS_LIST_END;
 
+    /* Sanity check, however these should never fail. However, as
+       this sanity check has been added here they have failed. */
+    if (!entry->nickname || !entry->username || !entry->userinfo)
+      continue;
+      
     /* Send WHOIS reply */
     idp = silc_id_payload_encode(entry->id, SILC_ID_CLIENT);
     tmp = silc_argument_get_first_arg(cmd->args, NULL);
     
-    /* XXX */
-    {
-      char nh[256], uh[256];
-      unsigned char idle[4];
-      SilcSocketConnection hsock;
-
-      memset(uh, 0, sizeof(uh));
-      memset(nh, 0, sizeof(nh));
-      
-      strncat(nh, entry->nickname, strlen(entry->nickname));
-      if (!strchr(entry->nickname, '@')) {
-       strncat(nh, "@", 1);
-       len = entry->router ? strlen(entry->router->server_name) :
-         strlen(server->server_name);
-       strncat(nh, entry->router ? entry->router->server_name :
-               server->server_name, len);
-      }
+    memset(uh, 0, sizeof(uh));
+    memset(nh, 0, sizeof(nh));
+    memset(idle, 0, sizeof(idle));
+    
+    strncat(nh, entry->nickname, strlen(entry->nickname));
+    if (!strchr(entry->nickname, '@')) {
+      strncat(nh, "@", 1);
+      len = entry->router ? strlen(entry->router->server_name) :
+       strlen(server->server_name);
+      strncat(nh, entry->router ? entry->router->server_name :
+             server->server_name, len);
+    }
       
-      strncat(uh, entry->username, strlen(entry->username));
-      if (!strchr(entry->username, '@')) {
-       strncat(uh, "@", 1);
-       hsock = (SilcSocketConnection)entry->connection;
-       len = strlen(hsock->hostname);
-       strncat(uh, hsock->hostname, len);
-      }
+    strncat(uh, entry->username, strlen(entry->username));
+    if (!strchr(entry->username, '@')) {
+      strncat(uh, "@", 1);
+      hsock = (SilcSocketConnection)entry->connection;
+      len = strlen(hsock->hostname);
+      strncat(uh, hsock->hostname, len);
+    }
+
+    channels = silc_server_get_client_channel_list(server, entry);
       
+    SILC_PUT32_MSB(entry->mode, mode);
+
+    if (entry->connection) {
       SILC_PUT32_MSB((time(NULL) - entry->data.last_receive), idle);
-      
-      /* XXX */
-      if (entry->userinfo)
-       packet = 
-         silc_command_reply_payload_encode_va(SILC_COMMAND_WHOIS,
-                                              status, ident, 5, 
-                                              2, idp->data, idp->len,
-                                              3, nh, strlen(nh),
-                                              4, uh, strlen(uh),
-                                              5, entry->userinfo, 
-                                              strlen(entry->userinfo),
-                                              7, idle, 4);
-      else
-       packet = 
-         silc_command_reply_payload_encode_va(SILC_COMMAND_WHOIS,
-                                              status, ident, 4, 
-                                              2, idp->data, idp->len,
-                                              3, nh, strlen(nh),
-                                              4, uh, strlen(uh),
-                                              7, idle, 4);
     }
+
+    if (channels)
+      packet = silc_command_reply_payload_encode_va(SILC_COMMAND_WHOIS,
+                                                   status, ident, 7, 
+                                                   2, idp->data, idp->len,
+                                                   3, nh, strlen(nh),
+                                                   4, uh, strlen(uh),
+                                                   5, entry->userinfo, 
+                                                   strlen(entry->userinfo),
+                                                   6, channels->data,
+                                                   channels->len,
+                                                   7, mode, 4,
+                                                   8, idle, 4);
+    else
+      packet = silc_command_reply_payload_encode_va(SILC_COMMAND_WHOIS,
+                                                   status, ident, 6, 
+                                                   2, idp->data, idp->len,
+                                                   3, nh, strlen(nh),
+                                                   4, uh, strlen(uh),
+                                                   5, entry->userinfo, 
+                                                   strlen(entry->userinfo),
+                                                   7, mode, 4,
+                                                   8, idle, 4);
     
     silc_server_packet_send(server, cmd->sock, SILC_PACKET_COMMAND_REPLY,
                            0, packet->data, packet->len, FALSE);
     
     silc_buffer_free(packet);
     silc_buffer_free(idp);
+    if (channels)
+      silc_buffer_free(channels);
   }
 }
 
@@ -501,22 +638,18 @@ silc_server_command_whois_from_client(SilcServerCommandContext cmd)
 {
   SilcServer server = cmd->server;
   char *nick = NULL, *server_name = NULL;
-  int count = 0, clients_count;
-  SilcClientID *client_id = NULL;
+  int count = 0, clients_count = 0;
   SilcClientEntry *clients = NULL, entry;
-  int ret = 0;
-
-  /* Parse the whois request */
-  if (!silc_server_command_whois_parse(cmd, &client_id, &nick, 
-                                      &server_name, &count))
-    return 0;
+  SilcClientID **client_id = NULL;
+  unsigned int client_id_count = 0;
+  int i, ret = 0;
 
   /* Protocol dictates that we must always send the received WHOIS request
      to our router if we are normal server, so let's do it now unless we
      are standalone. We will not send any replies to the client until we
      have received reply from the router. */
-  if (server->server_type == SILC_SERVER && 
-      !cmd->pending && !server->standalone) {
+  if (server->server_type == SILC_SERVER && !cmd->pending && 
+      !server->standalone) {
     SilcBuffer tmpbuf;
     unsigned short old_ident;
 
@@ -533,7 +666,9 @@ silc_server_command_whois_from_client(SilcServerCommandContext cmd)
     /* Reprocess this packet after received reply from router */
     silc_server_command_pending(server, SILC_COMMAND_WHOIS, 
                                silc_command_get_ident(cmd->payload),
-                               silc_server_command_whois, (void *)cmd);
+                               silc_server_command_destructor,
+                               silc_server_command_whois,
+                               silc_server_command_dup(cmd));
     cmd->pending = TRUE;
 
     silc_command_set_ident(cmd->payload, old_ident);
@@ -546,42 +681,71 @@ silc_server_command_whois_from_client(SilcServerCommandContext cmd)
   /* We are ready to process the command request. Let's search for the
      requested client and send reply to the requesting client. */
 
+  /* Parse the whois request */
+  if (!silc_server_command_whois_parse(cmd, &client_id, &client_id_count, 
+                                      &nick, &server_name, &count,
+                                      SILC_COMMAND_WHOIS))
+    return 0;
+
   /* Get all clients matching that ID or nickname from local list */
-  if (client_id) {
-    entry = silc_idlist_find_client_by_id(server->local_list, client_id, NULL);
-    if (entry) {
-      clients = silc_calloc(1, sizeof(*clients));
-      clients[0] = entry;
-      clients_count = 1;
+  if (client_id_count) {
+    /* Check all Client ID's received in the command packet */
+    for (i = 0; i < client_id_count; i++) {
+      entry = silc_idlist_find_client_by_id(server->local_list, 
+                                           client_id[i], NULL);
+      if (entry) {
+       clients = silc_realloc(clients, sizeof(*clients) * 
+                              (clients_count + 1));
+       clients[clients_count++] = entry;
+      }
     }
   } else {
     clients = silc_idlist_get_clients_by_nickname(server->local_list, 
                                                  nick, server_name,
                                                  &clients_count);
+    if (!clients)
+      clients = silc_idlist_get_clients_by_hash(server->local_list, 
+                                               nick, server->md5hash,
+                                               &clients_count);
   }
   
   /* Check global list as well */
   if (!clients) {
-    if (client_id) {
-      entry = silc_idlist_find_client_by_id(server->global_list, 
-                                           client_id, NULL);
-      if (entry) {
-       clients = silc_calloc(1, sizeof(*clients));
-       clients[0] = entry;
-       clients_count = 1;
+    if (client_id_count) {
+      /* Check all Client ID's received in the command packet */
+      for (i = 0; i < client_id_count; i++) {
+       entry = silc_idlist_find_client_by_id(server->global_list, 
+                                             client_id[i], NULL);
+       if (entry) {
+         clients = silc_realloc(clients, sizeof(*clients) * 
+                                (clients_count + 1));
+         clients[clients_count++] = entry;
+       }
       }
     } else {
       clients = silc_idlist_get_clients_by_nickname(server->global_list, 
                                                    nick, server_name,
                                                    &clients_count);
+      if (!clients)
+       clients = silc_idlist_get_clients_by_hash(server->global_list, 
+                                                 nick, server->md5hash,
+                                                 &clients_count);
     }
   }
   
   if (!clients) {
-    /* Such a client really does not exist in the SILC network. */
-    silc_server_command_send_status_data(cmd, SILC_COMMAND_WHOIS,
-                                        SILC_STATUS_ERR_NO_SUCH_NICK,
-                                        3, nick, strlen(nick));
+    /* Such client(s) really does not exist in the SILC network. */
+    if (!client_id_count) {
+      silc_server_command_send_status_data(cmd, SILC_COMMAND_WHOIS,
+                                          SILC_STATUS_ERR_NO_SUCH_NICK,
+                                          3, nick, strlen(nick));
+    } else {
+      SilcBuffer idp = silc_id_payload_encode(client_id[0], SILC_ID_CLIENT);
+      silc_server_command_send_status_data(cmd, SILC_COMMAND_WHOIS,
+                                          SILC_STATUS_ERR_NO_SUCH_CLIENT_ID,
+                                          2, idp->data, idp->len);
+      silc_buffer_free(idp);
+    }
     goto out;
   }
 
@@ -590,8 +754,7 @@ silc_server_command_whois_from_client(SilcServerCommandContext cmd)
      mandatory fields that WHOIS command reply requires. Check for these and
      make query from the server who owns the client if some fields are 
      missing. */
-  if (server->server_type == SILC_ROUTER &&
-      !silc_server_command_whois_check(cmd, clients, clients_count)) {
+  if (!silc_server_command_whois_check(cmd, clients, clients_count)) {
     ret = -1;
     goto out;
   }
@@ -600,10 +763,13 @@ silc_server_command_whois_from_client(SilcServerCommandContext cmd)
   silc_server_command_whois_send_reply(cmd, clients, clients_count);
 
  out:
+  if (client_id_count) {
+    for (i = 0; i < client_id_count; i++)
+      silc_free(client_id[i]);
+    silc_free(client_id);
+  }
   if (clients)
     silc_free(clients);
-  if (client_id)
-    silc_free(client_id);
   if (nick)
     silc_free(nick);
   if (server_name)
@@ -617,25 +783,31 @@ silc_server_command_whois_from_server(SilcServerCommandContext cmd)
 {
   SilcServer server = cmd->server;
   char *nick = NULL, *server_name = NULL;
-  int count = 0, clients_count;
-  SilcClientID *client_id = NULL;
+  int count = 0, clients_count = 0;
   SilcClientEntry *clients = NULL, entry;
-  int ret = 0;
+  SilcClientID **client_id = NULL;
+  unsigned int client_id_count = 0;
+  int i, ret = 0;
 
   /* Parse the whois request */
-  if (!silc_server_command_whois_parse(cmd, &client_id, &nick, 
-                                      &server_name, &count))
+  if (!silc_server_command_whois_parse(cmd, &client_id, &client_id_count, 
+                                      &nick, &server_name, &count,
+                                      SILC_COMMAND_WHOIS))
     return 0;
 
   /* Process the command request. Let's search for the requested client and
      send reply to the requesting server. */
 
-  if (client_id) {
-    entry = silc_idlist_find_client_by_id(server->local_list, client_id, NULL);
-    if (entry) {
-      clients = silc_calloc(1, sizeof(*clients));
-      clients[0] = entry;
-      clients_count = 1;
+  if (client_id_count) {
+    /* Check all Client ID's received in the command packet */
+    for (i = 0; i < client_id_count; i++) {
+      entry = silc_idlist_find_client_by_id(server->local_list, 
+                                           client_id[i], NULL);
+      if (entry) {
+       clients = silc_realloc(clients, sizeof(*clients) * 
+                              (clients_count + 1));
+       clients[clients_count++] = entry;
+      }
     }
   } else {
     clients = silc_idlist_get_clients_by_nickname(server->local_list, 
@@ -649,13 +821,16 @@ silc_server_command_whois_from_server(SilcServerCommandContext cmd)
   
   /* If we are router we will check our global list as well. */
   if (!clients && server->server_type == SILC_ROUTER) {
-    if (client_id) {
-      entry = silc_idlist_find_client_by_id(server->global_list, 
-                                           client_id, NULL);
-      if (entry) {
-       clients = silc_calloc(1, sizeof(*clients));
-       clients[0] = entry;
-       clients_count = 1;
+    if (client_id_count) {
+      /* Check all Client ID's received in the command packet */
+      for (i = 0; i < client_id_count; i++) {
+       entry = silc_idlist_find_client_by_id(server->global_list, 
+                                             client_id[i], NULL);
+       if (entry) {
+         clients = silc_realloc(clients, sizeof(*clients) * 
+                                (clients_count + 1));
+         clients[clients_count++] = entry;
+       }
       }
     } else {
       clients = silc_idlist_get_clients_by_nickname(server->global_list, 
@@ -670,12 +845,12 @@ silc_server_command_whois_from_server(SilcServerCommandContext cmd)
 
   if (!clients) {
     /* Such a client really does not exist in the SILC network. */
-    if (!client_id) {
+    if (!client_id_count) {
       silc_server_command_send_status_data(cmd, SILC_COMMAND_WHOIS,
                                           SILC_STATUS_ERR_NO_SUCH_NICK,
                                           3, nick, strlen(nick));
     } else {
-      SilcBuffer idp = silc_id_payload_encode(client_id, SILC_ID_CLIENT);
+      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);
@@ -689,8 +864,7 @@ silc_server_command_whois_from_server(SilcServerCommandContext cmd)
      mandatory fields that WHOIS command reply requires. Check for these and
      make query from the server who owns the client if some fields are 
      missing. */
-  if (server->server_type == SILC_ROUTER &&
-      !silc_server_command_whois_check(cmd, clients, clients_count)) {
+  if (!silc_server_command_whois_check(cmd, clients, clients_count)) {
     ret = -1;
     goto out;
   }
@@ -699,10 +873,13 @@ silc_server_command_whois_from_server(SilcServerCommandContext cmd)
   silc_server_command_whois_send_reply(cmd, clients, clients_count);
 
  out:
+  if (client_id_count) {
+    for (i = 0; i < client_id_count; i++)
+      silc_free(client_id[i]);
+    silc_free(client_id);
+  }
   if (clients)
     silc_free(clients);
-  if (client_id)
-    silc_free(client_id);
   if (nick)
     silc_free(nick);
   if (server_name)
@@ -717,145 +894,75 @@ silc_server_command_whois_from_server(SilcServerCommandContext cmd)
 SILC_SERVER_CMD_FUNC(whois)
 {
   SilcServerCommandContext cmd = (SilcServerCommandContext)context;
-  int ret;
+  int ret = 0;
 
-  SILC_SERVER_COMMAND_CHECK_ARGC(SILC_COMMAND_WHOIS, cmd, 1, 3);
+  SILC_SERVER_COMMAND_CHECK_ARGC(SILC_COMMAND_WHOIS, cmd, 1, 3328);
 
   if (cmd->sock->type == SILC_SOCKET_TYPE_CLIENT)
     ret = silc_server_command_whois_from_client(cmd);
-  else
+  else if ((cmd->sock->type == SILC_SOCKET_TYPE_SERVER) ||
+          (cmd->sock->type == SILC_SOCKET_TYPE_ROUTER))
     ret = silc_server_command_whois_from_server(cmd);
 
   if (!ret)
     silc_server_command_free(cmd);
 }
 
-SILC_SERVER_CMD_FUNC(whowas)
-{
-}
-
 /******************************************************************************
 
-                              IDENTIFY Functions
+                              WHOWAS Functions
 
 ******************************************************************************/
 
 static int
-silc_server_command_identify_parse(SilcServerCommandContext cmd,
-                                  SilcClientID **client_id,
-                                  char **nickname,
-                                  char **server_name,
-                                  int *count)
+silc_server_command_whowas_parse(SilcServerCommandContext cmd,
+                                char **nickname,
+                                char **server_name,
+                                int *count)
 {
   unsigned char *tmp;
   unsigned int len;
-  unsigned int argc = silc_argument_get_arg_num(cmd->args);
 
-  /* If client ID is in the command it must be used instead of nickname */
-  tmp = silc_argument_get_arg_type(cmd->args, 2, &len);
+  tmp = silc_argument_get_arg_type(cmd->args, 1, &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) {
-      if (strchr(tmp, '@')) {
-       len = strcspn(tmp, "@");
-       *nickname = silc_calloc(len + 1, sizeof(char));
-       memcpy(*nickname, tmp, len);
-       *server_name = silc_calloc(strlen(tmp) - len, sizeof(char));
-       memcpy(*server_name, tmp + len + 1, strlen(tmp) - len - 1);
-      } else {
-       *nickname = strdup(tmp);
-      }
-    } else {
-      silc_server_command_send_status_reply(cmd, SILC_COMMAND_IDENTIFY,
-                                           SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
-      return FALSE;
-    }
-  } else {
-    /* Command includes ID, use that */
-    *client_id = silc_id_payload_parse_id(tmp, len);
+    silc_server_command_send_status_reply(cmd, SILC_COMMAND_WHOWAS,
+                                         SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
+    return FALSE;
   }
 
+  /* Get the nickname@server string and parse it. */
+  if (strchr(tmp, '@')) {
+    len = strcspn(tmp, "@");
+    *nickname = silc_calloc(len + 1, sizeof(char));
+    memcpy(*nickname, tmp, len);
+    *server_name = silc_calloc(strlen(tmp) - len, sizeof(char));
+    memcpy(*server_name, tmp + len + 1, strlen(tmp) - len - 1);
+  } else {
+    *nickname = strdup(tmp);
+  }
   /* Get the max count of reply messages allowed */
-  if (argc == 3) {
-    tmp = silc_argument_get_arg_type(cmd->args, 3, NULL);
-    if (!tmp) {
-      silc_server_command_send_status_reply(cmd, SILC_COMMAND_IDENTIFY,
-                                           SILC_STATUS_ERR_TOO_MANY_PARAMS);
-      if (*nickname)
-       silc_free(*nickname);
-      if (*server_name)
-       silc_free(*server_name);
-
-      return FALSE;
-    }
+  tmp = silc_argument_get_arg_type(cmd->args, 2, NULL);
+  if (tmp)
     *count = atoi(tmp);
-  }
-
-  return TRUE;
-}
-
-/* Checks that all mandatory fields are present. If not then send WHOIS 
-   request to the server who owns the client. We use WHOIS because we want
-   to get as much information as possible at once. */
-
-static char
-silc_server_command_identify_check(SilcServerCommandContext cmd,
-                                  SilcClientEntry *clients,
-                                  unsigned int clients_count)
-{
-  SilcServer server = cmd->server;
-  int i;
-  SilcClientEntry entry;
-
-  for (i = 0; i < clients_count; i++) {
-    entry = clients[i];
-
-    if (!entry->nickname) {
-      SilcBuffer tmpbuf;
-      unsigned short old_ident;
-      
-      old_ident = silc_command_get_ident(cmd->payload);
-      silc_command_set_ident(cmd->payload, silc_rng_get_rn16(server->rng));
-      silc_command_set_command(cmd->payload, SILC_COMMAND_WHOIS);
-      tmpbuf = silc_command_payload_encode_payload(cmd->payload);
-      
-      /* Send WHOIS request. We send WHOIS since we're doing the requesting
-        now anyway so make it a good one. */
-      silc_server_packet_send(server, entry->router->connection,
-                             SILC_PACKET_COMMAND, cmd->packet->flags,
-                             tmpbuf->data, tmpbuf->len, TRUE);
-      
-      /* Reprocess this packet after received reply */
-      silc_server_command_pending(server, SILC_COMMAND_WHOIS, 
-                                 silc_command_get_ident(cmd->payload),
-                                 silc_server_command_identify, (void *)cmd);
-      cmd->pending = TRUE;
-      
-      /* Put old data back to the Command Payload we just changed */
-      silc_command_set_ident(cmd->payload, old_ident);
-      silc_command_set_command(cmd->payload, SILC_COMMAND_IDENTIFY);
-
-      silc_buffer_free(tmpbuf);
-      return FALSE;
-    }
-  }
+  else
+    *count = 0;
 
   return TRUE;
 }
 
 static void
-silc_server_command_identify_send_reply(SilcServerCommandContext cmd,
-                                       SilcClientEntry *clients,
-                                       unsigned int clients_count)
+silc_server_command_whowas_send_reply(SilcServerCommandContext cmd,
+                                     SilcClientEntry *clients,
+                                     unsigned int clients_count)
 {
   SilcServer server = cmd->server;
   char *tmp;
   int i, count = 0, len;
   SilcBuffer packet, idp;
-  SilcClientEntry entry;
+  SilcClientEntry entry = NULL;
   SilcCommandStatus status;
   unsigned short ident = silc_command_get_ident(cmd->payload);
+  char found = FALSE;
 
   status = SILC_STATUS_OK;
   if (clients_count > 1)
@@ -864,27 +971,40 @@ silc_server_command_identify_send_reply(SilcServerCommandContext cmd,
   for (i = 0; i < clients_count; i++) {
     entry = clients[i];
 
+    /* We will take only clients that are not valid anymore. They are the
+       ones that are not registered anymore but still have a ID. They
+       have disconnected us, and thus valid for WHOWAS. */
+    if (entry->data.registered == TRUE)
+      continue;
+    if (entry->id == NULL)
+      continue;
+
     if (count && i - 1 == count)
       break;
 
+    found = TRUE;
+
     if (clients_count > 2)
       status = SILC_STATUS_LIST_ITEM;
 
     if (clients_count > 1 && i == clients_count - 1)
       status = SILC_STATUS_LIST_END;
 
-    /* Send IDENTIFY reply */
+    /* Sanity check, however these should never fail. However, as
+       this sanity check has been added here they have failed. */
+    if (!entry->nickname || !entry->username)
+      continue;
+      
+    /* Send WHOWAS reply */
     idp = silc_id_payload_encode(entry->id, SILC_ID_CLIENT);
     tmp = silc_argument_get_first_arg(cmd->args, NULL);
     
-    /* XXX */
     {
       char nh[256], uh[256];
-      SilcSocketConnection hsock;
 
       memset(uh, 0, sizeof(uh));
       memset(nh, 0, sizeof(nh));
-      
+
       strncat(nh, entry->nickname, strlen(entry->nickname));
       if (!strchr(entry->nickname, '@')) {
        strncat(nh, "@", 1);
@@ -894,52 +1014,46 @@ silc_server_command_identify_send_reply(SilcServerCommandContext cmd,
                server->server_name, len);
       }
       
-      if (!entry->username) {
-       packet = silc_command_reply_payload_encode_va(SILC_COMMAND_IDENTIFY,
-                                                     SILC_STATUS_OK, ident, 2,
-                                                     2, idp->data, idp->len, 
-                                                     3, nh, strlen(nh));
-      } else {
-       strncat(uh, entry->username, strlen(entry->username));
-       if (!strchr(entry->username, '@')) {
-         strncat(uh, "@", 1);
-         hsock = (SilcSocketConnection)entry->connection;
-         len = strlen(hsock->hostname);
-         strncat(uh, hsock->hostname, len);
-       }
-      
-       packet = silc_command_reply_payload_encode_va(SILC_COMMAND_IDENTIFY,
-                                                     SILC_STATUS_OK, ident, 3,
-                                                     2, idp->data, idp->len, 
-                                                     3, nh, strlen(nh),
-                                                     4, uh, strlen(uh));
+      strncat(uh, entry->username, strlen(entry->username));
+      if (!strchr(entry->username, '@')) {
+       strncat(uh, "@", 1);
+       strcat(uh, "*private*");
       }
       
-      silc_server_packet_send(server, cmd->sock, SILC_PACKET_COMMAND_REPLY,
-                             0, packet->data, packet->len, FALSE);
-      
-      silc_buffer_free(packet);
-      silc_buffer_free(idp);
+      packet = 
+       silc_command_reply_payload_encode_va(SILC_COMMAND_WHOWAS,
+                                            status, ident, 4, 
+                                            2, idp->data, idp->len,
+                                            3, nh, strlen(nh),
+                                            4, uh, strlen(uh),
+                                            5, entry->userinfo, 
+                                            strlen(entry->userinfo));
     }
+    
+    silc_server_packet_send(server, cmd->sock, SILC_PACKET_COMMAND_REPLY,
+                           0, packet->data, packet->len, FALSE);
+    
+    silc_buffer_free(packet);
+    silc_buffer_free(idp);
   }
+
+  if (found == FALSE && entry)
+    silc_server_command_send_status_data(cmd, SILC_COMMAND_WHOWAS,
+                                        SILC_STATUS_ERR_NO_SUCH_NICK,
+                                        3, entry->nickname, 
+                                        strlen(entry->nickname));
 }
 
 static int
-silc_server_command_identify_from_client(SilcServerCommandContext cmd)
+silc_server_command_whowas_from_client(SilcServerCommandContext cmd)
 {
   SilcServer server = cmd->server;
   char *nick = NULL, *server_name = NULL;
-  int count = 0, clients_count;
-  SilcClientID *client_id = NULL;
-  SilcClientEntry *clients = NULL, entry;
+  int count = 0, clients_count = 0;
+  SilcClientEntry *clients = NULL;
   int ret = 0;
 
-  /* Parse the IDENTIFY request */
-  if (!silc_server_command_identify_parse(cmd, &client_id, &nick, 
-                                         &server_name, &count))
-    return 0;
-
-  /* Protocol dictates that we must always send the received IDENTIFY request
+  /* Protocol dictates that we must always send the received WHOWAS request
      to our router if we are normal server, so let's do it now unless we
      are standalone. We will not send any replies to the client until we
      have received reply from the router. */
@@ -952,16 +1066,18 @@ silc_server_command_identify_from_client(SilcServerCommandContext cmd)
     silc_command_set_ident(cmd->payload, silc_rng_get_rn16(server->rng));
     tmpbuf = silc_command_payload_encode_payload(cmd->payload);
 
-    /* Send IDENTIFY command to our router */
+    /* Send WHOWAS command to our router */
     silc_server_packet_send(server, (SilcSocketConnection)
                            server->router->connection,
                            SILC_PACKET_COMMAND, cmd->packet->flags,
                            tmpbuf->data, tmpbuf->len, TRUE);
 
     /* Reprocess this packet after received reply from router */
-    silc_server_command_pending(server, SILC_COMMAND_IDENTIFY
+    silc_server_command_pending(server, SILC_COMMAND_WHOWAS
                                silc_command_get_ident(cmd->payload),
-                               silc_server_command_identify, (void *)cmd);
+                               silc_server_command_destructor,
+                               silc_server_command_whois,
+                               silc_server_command_dup(cmd));
     cmd->pending = TRUE;
 
     silc_command_set_ident(cmd->payload, old_ident);
@@ -974,69 +1090,44 @@ silc_server_command_identify_from_client(SilcServerCommandContext cmd)
   /* We are ready to process the command request. Let's search for the
      requested client and send reply to the requesting client. */
 
-  /* Get all clients matching that ID or nickname from local list */
-  if (client_id) {
-    entry = silc_idlist_find_client_by_id(server->local_list, client_id, NULL);
-    if (entry) {
-      clients = silc_calloc(1, sizeof(*clients));
-      clients[0] = entry;
-      clients_count = 1;
-    }
-  } else {
-    clients = silc_idlist_get_clients_by_nickname(server->local_list, 
-                                                 nick, server_name,
-                                                 &clients_count);
-  }
+  /* Parse the whowas request */
+  if (!silc_server_command_whowas_parse(cmd, &nick, &server_name, &count))
+    return 0;
+
+  /* Get all clients matching that nickname from local list */
+  clients = silc_idlist_get_clients_by_nickname(server->local_list, 
+                                               nick, server_name,
+                                               &clients_count);
+  if (!clients)
+    clients = silc_idlist_get_clients_by_hash(server->local_list, 
+                                             nick, server->md5hash,
+                                             &clients_count);
   
   /* Check global list as well */
   if (!clients) {
-    if (client_id) {
-      entry = silc_idlist_find_client_by_id(server->global_list, 
-                                           client_id, NULL);
-      if (entry) {
-       clients = silc_calloc(1, sizeof(*clients));
-       clients[0] = entry;
-       clients_count = 1;
-      }
-    } else {
-      clients = silc_idlist_get_clients_by_nickname(server->global_list, 
-                                                   nick, server_name,
-                                                   &clients_count);
-    }
+    clients = silc_idlist_get_clients_by_nickname(server->global_list, 
+                                                 nick, server_name,
+                                                 &clients_count);
+    if (!clients)
+      clients = silc_idlist_get_clients_by_hash(server->global_list, 
+                                               nick, server->md5hash,
+                                               &clients_count);
   }
   
   if (!clients) {
-    /* Such a client really does not exist in the SILC network. */
-    if (!client_id) {
-      silc_server_command_send_status_data(cmd, SILC_COMMAND_IDENTIFY,
-                                          SILC_STATUS_ERR_NO_SUCH_NICK,
-                                          3, nick, strlen(nick));
-    } else {
-      SilcBuffer idp = silc_id_payload_encode(client_id, SILC_ID_CLIENT);
-      silc_server_command_send_status_data(cmd, SILC_COMMAND_IDENTIFY,
-                                          SILC_STATUS_ERR_NO_SUCH_CLIENT_ID,
-                                          2, idp->data, idp->len);
-      silc_buffer_free(idp);
-    }
-    goto out;
-  }
-
-  /* Check that all mandatory fields are present and request those data
-     from the server who owns the client if necessary. */
-  if (!cmd->pending && server->server_type == SILC_ROUTER &&
-      !silc_server_command_identify_check(cmd, clients, clients_count)) {
-    ret = -1;
+    /* Such client(s) really does not exist in the SILC network. */
+    silc_server_command_send_status_data(cmd, SILC_COMMAND_WHOWAS,
+                                        SILC_STATUS_ERR_NO_SUCH_NICK,
+                                        3, nick, strlen(nick));
     goto out;
   }
 
   /* Send the command reply to the client */
-  silc_server_command_identify_send_reply(cmd, clients, clients_count);
+  silc_server_command_whowas_send_reply(cmd, clients, clients_count);
 
  out:
   if (clients)
     silc_free(clients);
-  if (client_id)
-    silc_free(client_id);
   if (nick)
     silc_free(nick);
   if (server_name)
@@ -1046,85 +1137,54 @@ silc_server_command_identify_from_client(SilcServerCommandContext cmd)
 }
 
 static int
-silc_server_command_identify_from_server(SilcServerCommandContext cmd)
+silc_server_command_whowas_from_server(SilcServerCommandContext cmd)
 {
   SilcServer server = cmd->server;
   char *nick = NULL, *server_name = NULL;
-  int count = 0, clients_count;
-  SilcClientID *client_id = NULL;
-  SilcClientEntry *clients = NULL, entry;
+  int count = 0, clients_count = 0;
+  SilcClientEntry *clients = NULL;
   int ret = 0;
 
-  /* Parse the IDENTIFY request */
-  if (!silc_server_command_identify_parse(cmd, &client_id, &nick, 
-                                         &server_name, &count))
+  /* Parse the whowas request */
+  if (!silc_server_command_whowas_parse(cmd, &nick, &server_name, &count))
     return 0;
 
   /* Process the command request. Let's search for the requested client and
      send reply to the requesting server. */
 
-  if (client_id) {
-    entry = silc_idlist_find_client_by_id(server->local_list, client_id, NULL);
-    if (entry) {
-      clients = silc_calloc(1, sizeof(*clients));
-      clients[0] = entry;
-      clients_count = 1;
-    }
-  } else {
-    clients = silc_idlist_get_clients_by_nickname(server->local_list, 
-                                                 nick, server_name,
-                                                 &clients_count);
-    if (!clients)
-      clients = silc_idlist_get_clients_by_hash(server->local_list, 
-                                               nick, server->md5hash,
+  clients = silc_idlist_get_clients_by_nickname(server->local_list, 
+                                               nick, server_name,
                                                &clients_count);
-  }
+  if (!clients)
+    clients = silc_idlist_get_clients_by_hash(server->local_list, 
+                                             nick, server->md5hash,
+                                             &clients_count);
   
   /* If we are router we will check our global list as well. */
   if (!clients && server->server_type == SILC_ROUTER) {
-    if (client_id) {
-      entry = silc_idlist_find_client_by_id(server->global_list, 
-                                           client_id, NULL);
-      if (entry) {
-       clients = silc_calloc(1, sizeof(*clients));
-       clients[0] = entry;
-       clients_count = 1;
-      }
-    } else {
-      clients = silc_idlist_get_clients_by_nickname(server->global_list, 
-                                                   nick, server_name,
-                                                   &clients_count);
-      if (!clients)
-       clients = silc_idlist_get_clients_by_hash(server->global_list, 
-                                                 nick, server->md5hash,
+    clients = silc_idlist_get_clients_by_nickname(server->global_list, 
+                                                 nick, server_name,
                                                  &clients_count);
-    }
+    if (!clients)
+      clients = silc_idlist_get_clients_by_hash(server->global_list, 
+                                               nick, server->md5hash,
+                                               &clients_count);
   }
 
   if (!clients) {
     /* Such a client really does not exist in the SILC network. */
-    silc_server_command_send_status_data(cmd, SILC_COMMAND_IDENTIFY,
+    silc_server_command_send_status_data(cmd, SILC_COMMAND_WHOWAS,
                                         SILC_STATUS_ERR_NO_SUCH_NICK,
                                         3, nick, strlen(nick));
     goto out;
   }
 
-  /* Check that all mandatory fields are present and request those data
-     from the server who owns the client if necessary. */
-  if (!cmd->pending && server->server_type == SILC_ROUTER &&
-      !silc_server_command_identify_check(cmd, clients, clients_count)) {
-    ret = -1;
-    goto out;
-  }
-
-  /* Send the command reply */
-  silc_server_command_identify_send_reply(cmd, clients, clients_count);
+  /* Send the command reply to the client */
+  silc_server_command_whowas_send_reply(cmd, clients, clients_count);
 
  out:
   if (clients)
     silc_free(clients);
-  if (client_id)
-    silc_free(client_id);
   if (nick)
     silc_free(nick);
   if (server_name)
@@ -1133,1592 +1193,2629 @@ silc_server_command_identify_from_server(SilcServerCommandContext cmd)
   return ret;
 }
 
-SILC_SERVER_CMD_FUNC(identify)
+/* Server side of command WHOWAS. */
+
+SILC_SERVER_CMD_FUNC(whowas)
 {
   SilcServerCommandContext cmd = (SilcServerCommandContext)context;
-  int ret;
+  int ret = 0;
 
-  SILC_SERVER_COMMAND_CHECK_ARGC(SILC_COMMAND_IDENTIFY, cmd, 1, 3);
+  SILC_SERVER_COMMAND_CHECK_ARGC(SILC_COMMAND_WHOWAS, cmd, 1, 2);
 
   if (cmd->sock->type == SILC_SOCKET_TYPE_CLIENT)
-    ret = silc_server_command_identify_from_client(cmd);
-  else
-    ret = silc_server_command_identify_from_server(cmd);
+    ret = silc_server_command_whowas_from_client(cmd);
+  else if ((cmd->sock->type == SILC_SOCKET_TYPE_SERVER) ||
+          (cmd->sock->type == SILC_SOCKET_TYPE_ROUTER))
+    ret = silc_server_command_whowas_from_server(cmd);
 
   if (!ret)
     silc_server_command_free(cmd);
 }
 
-/* Checks string for bad characters and returns TRUE if they are found. */
+/******************************************************************************
 
-static int silc_server_command_bad_chars(char *nick)
-{
-  if (strchr(nick, '\\')) return TRUE;
-  if (strchr(nick, '\"')) return TRUE;
-  if (strchr(nick, '´')) return TRUE;
-  if (strchr(nick, '`')) return TRUE;
-  if (strchr(nick, '\'')) return TRUE;
-  if (strchr(nick, '*')) return TRUE;
-  if (strchr(nick, '/')) return TRUE;
-  if (strchr(nick, '@')) return TRUE;
+                              IDENTIFY Functions
 
-  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. */
+/* Checks that all mandatory fields are present. If not then send WHOIS 
+   request to the server who owns the client. We use WHOIS because we want
+   to get as much information as possible at once. */
 
-SILC_SERVER_CMD_FUNC(nick)
+static char
+silc_server_command_identify_check(SilcServerCommandContext cmd,
+                                  SilcClientEntry *clients,
+                                  unsigned int clients_count)
 {
-  SilcServerCommandContext cmd = (SilcServerCommandContext)context;
-  SilcClientEntry client = (SilcClientEntry)cmd->sock->user_data;
   SilcServer server = cmd->server;
-  SilcBuffer packet, nidp, oidp;
-  SilcClientID *new_id;
-  char *nick;
-
-  SILC_SERVER_COMMAND_CHECK_ARGC(SILC_COMMAND_NICK, cmd, 1, 1);
+  int i;
+  SilcClientEntry entry;
 
-  /* Check nickname */
-  nick = silc_argument_get_arg_type(cmd->args, 1, NULL);
-  if (silc_server_command_bad_chars(nick) == TRUE) {
-    silc_server_command_send_status_reply(cmd, SILC_COMMAND_NICK,
-                                         SILC_STATUS_ERR_BAD_NICKNAME);
-    goto out;
-  }
+  for (i = 0; i < clients_count; i++) {
+    entry = clients[i];
 
-  /* Create new Client ID */
-  silc_id_create_client_id(cmd->server->id, cmd->server->rng, 
-                          cmd->server->md5hash, nick,
-                          &new_id);
+    if (!entry->nickname) {
+      SilcBuffer tmpbuf;
+      unsigned short old_ident;
+      
+      if (!entry->router)
+       continue;
+      
+      old_ident = silc_command_get_ident(cmd->payload);
+      silc_command_set_ident(cmd->payload, silc_rng_get_rn16(server->rng));
+      silc_command_set_command(cmd->payload, SILC_COMMAND_WHOIS);
+      tmpbuf = silc_command_payload_encode_payload(cmd->payload);
+      
+      /* Send WHOIS request. We send WHOIS since we're doing the requesting
+        now anyway so make it a good one. */
+      silc_server_packet_send(server, entry->router->connection,
+                             SILC_PACKET_COMMAND, cmd->packet->flags,
+                             tmpbuf->data, tmpbuf->len, TRUE);
+      
+      /* Reprocess this packet after received reply */
+      silc_server_command_pending(server, SILC_COMMAND_WHOIS, 
+                                 silc_command_get_ident(cmd->payload),
+                                 silc_server_command_destructor,
+                                 silc_server_command_identify,
+                                 silc_server_command_dup(cmd));
 
-  /* Send notify about nickname change to our router. We send the new
-     ID and ask to replace it with the old one. If we are router the
-     packet is broadcasted. */
-  if (!cmd->server->standalone)
-    silc_server_send_replace_id(server, server->router->connection, 
-                               server->server_type == SILC_SERVER ? 
-                               FALSE : TRUE, client->id,
-                               SILC_ID_CLIENT, SILC_ID_CLIENT_LEN,
-                               new_id, SILC_ID_CLIENT, SILC_ID_CLIENT_LEN);
+      cmd->pending = TRUE;
+      
+      /* Put old data back to the Command Payload we just changed */
+      silc_command_set_ident(cmd->payload, old_ident);
+      silc_command_set_command(cmd->payload, SILC_COMMAND_IDENTIFY);
 
-  /* Remove old cache entry */
-  silc_idcache_del_by_id(server->local_list->clients, SILC_ID_CLIENT, 
-                        client->id); 
+      silc_buffer_free(tmpbuf);
+      return FALSE;
+    }
+  }
 
-  oidp = silc_id_payload_encode(client->id, SILC_ID_CLIENT);
+  return TRUE;
+}
 
-  /* Free old ID */
-  if (client->id) {
-    memset(client->id, 0, SILC_ID_CLIENT_LEN);
-    silc_free(client->id);
-  }
+static void
+silc_server_command_identify_send_reply(SilcServerCommandContext cmd,
+                                       SilcClientEntry *clients,
+                                       unsigned int clients_count)
+{
+  SilcServer server = cmd->server;
+  char *tmp;
+  int i, count = 0, len;
+  SilcBuffer packet, idp;
+  SilcClientEntry entry;
+  SilcCommandStatus status;
+  unsigned short ident = silc_command_get_ident(cmd->payload);
 
-  /* Save the nickname as this client is our local client */
-  if (client->nickname)
-    silc_free(client->nickname);
+  status = SILC_STATUS_OK;
+  if (clients_count > 1)
+    status = SILC_STATUS_LIST_START;
 
-  client->nickname = strdup(nick);
-  client->id = new_id;
+  for (i = 0; i < clients_count; i++) {
+    entry = clients[i];
 
-  /* Update client cache */
-  silc_idcache_add(server->local_list->clients, client->nickname, 
-                  SILC_ID_CLIENT, client->id, (void *)client, TRUE);
+    if (entry->connection && entry->data.registered == FALSE) {
+      if (clients_count == 1)
+       silc_server_command_send_status_data(cmd, SILC_COMMAND_IDENTIFY,
+                                            SILC_STATUS_ERR_NO_SUCH_NICK,
+                                            3, entry->nickname, 
+                                            strlen(entry->nickname));
+      continue;
+    }
 
-  nidp = silc_id_payload_encode(client->id, SILC_ID_CLIENT);
+    if (count && i - 1 == count)
+      break;
 
-  /* Send NICK_CHANGE notify */
-  silc_server_send_notify_on_channels(server, client, 
-                                     SILC_NOTIFY_TYPE_NICK_CHANGE, 2,
-                                     oidp->data, oidp->len, 
-                                     nidp->data, nidp->len);
+    if (clients_count > 2)
+      status = SILC_STATUS_LIST_ITEM;
 
-  /* Send the new Client ID as reply command back to client */
-  packet = silc_command_reply_payload_encode_va(SILC_COMMAND_NICK, 
-                                               SILC_STATUS_OK, 0, 1, 
-                                               2, nidp->data, nidp->len);
-  silc_server_packet_send(cmd->server, cmd->sock, SILC_PACKET_COMMAND_REPLY,
-                         0, packet->data, packet->len, FALSE);
+    if (clients_count > 1 && i == clients_count - 1)
+      status = SILC_STATUS_LIST_END;
 
-  silc_buffer_free(packet);
-  silc_buffer_free(nidp);
-  silc_buffer_free(oidp);
-  
- out:
-  silc_server_command_free(cmd);
-}
+    /* Send IDENTIFY reply */
+    idp = silc_id_payload_encode(entry->id, SILC_ID_CLIENT);
+    tmp = silc_argument_get_first_arg(cmd->args, NULL);
+    
+    /* XXX */
+    {
+      char nh[256], uh[256];
+      SilcSocketConnection hsock;
 
-SILC_SERVER_CMD_FUNC(list)
-{
+      memset(uh, 0, sizeof(uh));
+      memset(nh, 0, sizeof(nh));
+      
+      strncat(nh, entry->nickname, strlen(entry->nickname));
+      if (!strchr(entry->nickname, '@')) {
+       strncat(nh, "@", 1);
+       len = entry->router ? strlen(entry->router->server_name) :
+         strlen(server->server_name);
+       strncat(nh, entry->router ? entry->router->server_name :
+               server->server_name, len);
+      }
+      
+      if (!entry->username) {
+       packet = silc_command_reply_payload_encode_va(SILC_COMMAND_IDENTIFY,
+                                                     status, ident, 2,
+                                                     2, idp->data, idp->len, 
+                                                     3, nh, strlen(nh));
+      } else {
+       strncat(uh, entry->username, strlen(entry->username));
+       if (!strchr(entry->username, '@')) {
+         strncat(uh, "@", 1);
+         hsock = (SilcSocketConnection)entry->connection;
+         len = strlen(hsock->hostname);
+         strncat(uh, hsock->hostname, len);
+       }
+      
+       packet = silc_command_reply_payload_encode_va(SILC_COMMAND_IDENTIFY,
+                                                     status, ident, 3,
+                                                     2, idp->data, idp->len, 
+                                                     3, nh, strlen(nh),
+                                                     4, uh, strlen(uh));
+      }
+      
+      silc_server_packet_send(server, cmd->sock, SILC_PACKET_COMMAND_REPLY,
+                             0, packet->data, packet->len, FALSE);
+      
+      silc_buffer_free(packet);
+      silc_buffer_free(idp);
+    }
+  }
 }
 
-/* Server side of TOPIC command. Sets topic for channel and/or returns
-   current topic to client. */
-
-SILC_SERVER_CMD_FUNC(topic)
+static int
+silc_server_command_identify_from_client(SilcServerCommandContext cmd)
 {
-  SilcServerCommandContext cmd = (SilcServerCommandContext)context;
   SilcServer server = cmd->server;
-  SilcClientEntry client = (SilcClientEntry)cmd->sock->user_data;
-  SilcChannelID *channel_id;
-  SilcChannelEntry channel;
-  SilcChannelClientEntry chl;
-  SilcBuffer packet, idp;
-  unsigned char *tmp;
-  unsigned int argc, tmp_len;
+  char *nick = NULL, *server_name = NULL;
+  int count = 0, clients_count = 0; 
+  SilcClientEntry *clients = NULL, entry;
+  SilcClientID **client_id = NULL;
+  unsigned int client_id_count = 0;
+  int i, ret = 0;
 
-  SILC_SERVER_COMMAND_CHECK_ARGC(SILC_COMMAND_TOPIC, cmd, 1, 2);
+  /* Protocol dictates that we must always send the received IDENTIFY request
+     to our router if we are normal server, so let's do it now unless we
+     are standalone. We will not send any replies to the client until we
+     have received reply from the router. */
+  if (server->server_type == SILC_SERVER && 
+      !cmd->pending && !server->standalone) {
+    SilcBuffer tmpbuf;
+    unsigned short old_ident;
 
-  argc = silc_argument_get_arg_num(cmd->args);
+    old_ident = silc_command_get_ident(cmd->payload);
+    silc_command_set_ident(cmd->payload, silc_rng_get_rn16(server->rng));
+    tmpbuf = silc_command_payload_encode_payload(cmd->payload);
 
-  /* Get Channel ID */
-  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);
-    goto out;
-  }
-  channel_id = silc_id_payload_parse_id(tmp, tmp_len);
+    /* Send IDENTIFY command to our router */
+    silc_server_packet_send(server, (SilcSocketConnection)
+                           server->router->connection,
+                           SILC_PACKET_COMMAND, cmd->packet->flags,
+                           tmpbuf->data, tmpbuf->len, TRUE);
 
-  /* Check whether the channel exists */
-  channel = silc_idlist_find_channel_by_id(server->local_list, 
-                                          channel_id, NULL);
-  if (!channel) {
-    silc_server_command_send_status_reply(cmd, SILC_COMMAND_TOPIC,
-                                         SILC_STATUS_ERR_NO_SUCH_CHANNEL);
+    /* Reprocess this packet after received reply from router */
+    silc_server_command_pending(server, SILC_COMMAND_IDENTIFY, 
+                               silc_command_get_ident(cmd->payload),
+                               silc_server_command_destructor,
+                               silc_server_command_identify,
+                               silc_server_command_dup(cmd));
+    cmd->pending = TRUE;
+
+    silc_command_set_ident(cmd->payload, old_ident);
+
+    silc_buffer_free(tmpbuf);
+    ret = -1;
     goto out;
   }
 
-  if (argc > 1) {
-    /* Get the 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);
-      goto out;
-    }
+  /* We are ready to process the command request. Let's search for the
+     requested client and send reply to the requesting client. */
 
-    if (strlen(tmp) > 256) {
-      silc_server_command_send_status_reply(cmd, SILC_COMMAND_TOPIC,
-                                           SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
-      goto out;
-    }
+  /* Parse the IDENTIFY request */
+  if (!silc_server_command_whois_parse(cmd, &client_id, &client_id_count,
+                                      &nick, &server_name, &count,
+                                      SILC_COMMAND_IDENTIFY))
+    return 0;
 
-    /* See whether has rights to change topic */
-    silc_list_start(channel->user_list);
-    while ((chl = silc_list_get(channel->user_list)) != SILC_LIST_END) {
-      if (chl->client == client) {
-       if (chl->mode == SILC_CHANNEL_UMODE_NONE) {
-         silc_server_command_send_status_reply(cmd, SILC_COMMAND_TOPIC,
-                                               SILC_STATUS_ERR_NO_CHANNEL_PRIV);
-         goto out;
-       } else {
-         break;
+  /* Get all clients matching that ID or nickname from local list */
+  if (client_id_count) { 
+    /* Check all Client ID's received in the command packet */
+    for (i = 0; i < client_id_count; i++) {
+      entry = silc_idlist_find_client_by_id(server->local_list, 
+                                           client_id[i], NULL);
+      if (entry) {
+       clients = silc_realloc(clients, sizeof(*clients) * 
+                              (clients_count + 1));
+       clients[clients_count++] = entry;
+      }
+    }
+  } else {
+    clients = silc_idlist_get_clients_by_nickname(server->local_list, 
+                                                 nick, server_name,
+                                                 &clients_count);
+    if (!clients)
+      clients = silc_idlist_get_clients_by_hash(server->local_list, 
+                                               nick, server->md5hash,
+                                               &clients_count);
+  }
+  
+  /* Check global list as well */
+  if (!clients) {
+    if (client_id_count) {
+      /* Check all Client ID's received in the command packet */
+      for (i = 0; i < client_id_count; i++) {
+       entry = silc_idlist_find_client_by_id(server->global_list, 
+                                             client_id[i], NULL);
+       if (entry) {
+         clients = silc_realloc(clients, sizeof(*clients) * 
+                                (clients_count + 1));
+         clients[clients_count++] = entry;
        }
       }
+    } else {
+      clients = silc_idlist_get_clients_by_nickname(server->global_list, 
+                                                   nick, server_name,
+                                                   &clients_count);
+      if (!clients)
+       clients = silc_idlist_get_clients_by_hash(server->global_list, 
+                                                 nick, server->md5hash,
+                                                 &clients_count);
     }
-
-    /* Set the topic for channel */
-    if (channel->topic)
-      silc_free(channel->topic);
-    channel->topic = strdup(tmp);
-
-    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, channel, TRUE,
-                                      SILC_NOTIFY_TYPE_TOPIC_SET, 2,
-                                      idp->data, idp->len,
-                                      channel->topic, strlen(channel->topic));
-    silc_buffer_free(idp);
+  }
+  
+  if (!clients) {
+    /* Such a client really does not exist in the SILC network. */
+    if (!client_id_count) {
+      silc_server_command_send_status_data(cmd, SILC_COMMAND_IDENTIFY,
+                                          SILC_STATUS_ERR_NO_SUCH_NICK,
+                                          3, nick, strlen(nick));
+    } else {
+      SilcBuffer idp = silc_id_payload_encode(client_id[0], SILC_ID_CLIENT);
+      silc_server_command_send_status_data(cmd, SILC_COMMAND_IDENTIFY,
+                                          SILC_STATUS_ERR_NO_SUCH_CLIENT_ID,
+                                          2, idp->data, idp->len);
+      silc_buffer_free(idp);
+    }
+    goto out;
   }
 
-  /* Send the topic to client as reply packet */
-  idp = silc_id_payload_encode(channel_id, SILC_ID_CHANNEL);
-  if (channel->topic)
-    packet = silc_command_reply_payload_encode_va(SILC_COMMAND_TOPIC, 
-                                                 SILC_STATUS_OK, 0, 2, 
-                                                 2, idp->data, idp->len,
-                                                 3, channel->topic, 
-                                                 strlen(channel->topic));
-  else
-    packet = silc_command_reply_payload_encode_va(SILC_COMMAND_TOPIC, 
-                                                 SILC_STATUS_OK, 0, 1, 
-                                                 2, idp->data, idp->len);
-  silc_server_packet_send(cmd->server, cmd->sock, SILC_PACKET_COMMAND_REPLY,
-                         0, packet->data, packet->len, FALSE);
+  /* Check that all mandatory fields are present and request those data
+     from the server who owns the client if necessary. */
+  if (!silc_server_command_identify_check(cmd, clients, clients_count)) {
+    ret = -1;
+    goto out;
+  }
 
-  silc_buffer_free(packet);
-  silc_buffer_free(idp);
-  silc_free(channel_id);
+  /* Send the command reply to the client */
+  silc_server_command_identify_send_reply(cmd, clients, clients_count);
 
  out:
-  silc_server_command_free(cmd);
-}
+  if (client_id_count) {
+    for (i = 0; i < client_id_count; i++)
+      silc_free(client_id[i]);
+    silc_free(client_id);
+  }
+  if (clients)
+    silc_free(clients);
+  if (nick)
+    silc_free(nick);
+  if (server_name)
+    silc_free(server_name);
 
-/* Server side of INVITE command. Invites some client to join some channel. */
+  return ret;
+}
 
-SILC_SERVER_CMD_FUNC(invite)
+static int
+silc_server_command_identify_from_server(SilcServerCommandContext cmd)
 {
-  SilcServerCommandContext cmd = (SilcServerCommandContext)context;
   SilcServer server = cmd->server;
-  SilcSocketConnection sock = cmd->sock, dest_sock;
-  SilcClientEntry sender, dest;
-  SilcClientID *dest_id;
-  SilcChannelEntry channel;
-  SilcChannelID *channel_id;
-  SilcBuffer sidp;
-  unsigned char *tmp;
-  unsigned int len;
-
-  SILC_SERVER_COMMAND_CHECK_ARGC(SILC_COMMAND_INVITE, cmd, 1, 2);
+  char *nick = NULL, *server_name = NULL;
+  int count = 0, clients_count = 0;
+  SilcClientEntry *clients = NULL, entry;
+  SilcClientID **client_id = NULL;
+  unsigned int client_id_count = 0;
+  int i, ret = 0;
 
-  /* Get destination ID */
-  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_CLIENT_ID);
-    goto out;
-  }
-  dest_id = silc_id_payload_parse_id(tmp, len);
+  /* Parse the IDENTIFY request */
+  if (!silc_server_command_whois_parse(cmd, &client_id, &client_id_count,
+                                      &nick, &server_name, &count,
+                                      SILC_COMMAND_IDENTIFY))
+    return 0;
 
-  /* Get Channel ID */
-  tmp = silc_argument_get_arg_type(cmd->args, 2, &len);
-  if (!tmp) {
-    silc_server_command_send_status_reply(cmd, SILC_COMMAND_INVITE,
-                                         SILC_STATUS_ERR_NO_CHANNEL_ID);
-    goto out;
-  }
-  channel_id = silc_id_payload_parse_id(tmp, len);
+  /* Process the command request. Let's search for the requested client and
+     send reply to the requesting server. */
 
-  /* Check whether the channel exists */
-  channel = silc_idlist_find_channel_by_id(server->local_list, 
-                                          channel_id, NULL);
-  if (!channel) {
-    silc_server_command_send_status_reply(cmd, SILC_COMMAND_INVITE,
-                                         SILC_STATUS_ERR_NO_SUCH_CHANNEL);
-    goto out;
+  if (client_id_count) {
+    /* Check all Client ID's received in the command packet */
+    for (i = 0; i < client_id_count; i++) {
+      entry = silc_idlist_find_client_by_id(server->local_list, 
+                                           client_id[i], NULL);
+      if (entry) {
+       clients = silc_realloc(clients, sizeof(*clients) * 
+                              (clients_count + 1));
+       clients[clients_count++] = entry;
+      }
+    }
+  } else {
+    clients = silc_idlist_get_clients_by_nickname(server->local_list, 
+                                                 nick, server_name,
+                                                 &clients_count);
+    if (!clients)
+      clients = silc_idlist_get_clients_by_hash(server->local_list, 
+                                               nick, server->md5hash,
+                                               &clients_count);
+  }
+  
+  /* If we are router we will check our global list as well. */
+  if (!clients && server->server_type == SILC_ROUTER) {
+    if (client_id_count) {
+      /* Check all Client ID's received in the command packet */
+      for (i = 0; i < client_id_count; i++) {
+       entry = silc_idlist_find_client_by_id(server->global_list, 
+                                             client_id[i], NULL);
+       if (entry) {
+         clients = silc_realloc(clients, sizeof(*clients) * 
+                                (clients_count + 1));
+         clients[clients_count++] = entry;
+       }
+      }
+    } else {
+      clients = silc_idlist_get_clients_by_nickname(server->global_list, 
+                                                   nick, server_name,
+                                                   &clients_count);
+      if (!clients)
+       clients = silc_idlist_get_clients_by_hash(server->global_list, 
+                                                 nick, server->md5hash,
+                                                 &clients_count);
+    }
   }
 
-  /* Check whether the sender of this command is on the channel. */
-  sender = (SilcClientEntry)sock->user_data;
-  if (!silc_server_client_on_channel(sender, channel)) {
-    silc_server_command_send_status_reply(cmd, SILC_COMMAND_INVITE,
-                                         SILC_STATUS_ERR_NOT_ON_CHANNEL);
+  if (!clients) {
+    /* Such a client really does not exist in the SILC network. */
+    if (!client_id_count) {
+      silc_server_command_send_status_data(cmd, SILC_COMMAND_IDENTIFY,
+                                          SILC_STATUS_ERR_NO_SUCH_NICK,
+                                          3, nick, strlen(nick));
+    } else {
+      SilcBuffer idp = silc_id_payload_encode(client_id[0], SILC_ID_CLIENT);
+      silc_server_command_send_status_data(cmd, SILC_COMMAND_IDENTIFY,
+                                          SILC_STATUS_ERR_NO_SUCH_CLIENT_ID,
+                                          2, idp->data, idp->len);
+      silc_buffer_free(idp);
+    }
     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) {
-    SilcChannelClientEntry chl;
-
-    silc_list_start(channel->user_list);
-    while ((chl = silc_list_get(channel->user_list)) != SILC_LIST_END)
-      if (chl->client == sender) {
-       if (chl->mode == SILC_CHANNEL_UMODE_NONE) {
-         silc_server_command_send_status_reply(cmd, SILC_COMMAND_INVITE,
-                                       SILC_STATUS_ERR_NO_CHANNEL_PRIV);
-         goto out;
-       }
-       break;
-      }
+  /* Check that all mandatory fields are present and request those data
+     from the server who owns the client if necessary. */
+  if (!silc_server_command_identify_check(cmd, clients, clients_count)) {
+    ret = -1;
+    goto out;
   }
 
-  /* Find the connection data for the destination. If it is local we will
-     send it directly otherwise we will send it to router for routing. */
-  dest = silc_idlist_find_client_by_id(server->local_list, dest_id, NULL);
-  if (dest)
-    dest_sock = (SilcSocketConnection)dest->connection;
-  else
-    dest_sock = silc_server_route_get(server, dest_id, SILC_ID_CLIENT);
+  /* Send the command reply */
+  silc_server_command_identify_send_reply(cmd, clients, clients_count);
 
-  /* Check whether the requested client is already on the channel. */
-  /* XXX if we are normal server we don't know about global clients on
-     the channel thus we must request it (NAMES command), check from
-     local cache as well. */
-  if (silc_server_client_on_channel(dest, channel)) {
-    silc_server_command_send_status_reply(cmd, SILC_COMMAND_INVITE,
-                                         SILC_STATUS_ERR_USER_ON_CHANNEL);
-    goto out;
+ out:
+  if (client_id_count) {
+    for (i = 0; i < client_id_count; i++)
+      silc_free(client_id[i]);
+    silc_free(client_id);
   }
+  if (clients)
+    silc_free(clients);
+  if (nick)
+    silc_free(nick);
+  if (server_name)
+    silc_free(server_name);
 
-  sidp = silc_id_payload_encode(sender->id, SILC_ID_CLIENT);
+  return ret;
+}
 
-  /* Send notify to the client that is invited to the channel */
-  silc_server_send_notify_dest(server, dest_sock, dest_id, SILC_ID_CLIENT,
-                              SILC_NOTIFY_TYPE_INVITE, 2, 
-                              sidp->data, sidp->len, tmp, len);
+SILC_SERVER_CMD_FUNC(identify)
+{
+  SilcServerCommandContext cmd = (SilcServerCommandContext)context;
+  int ret = 0;
 
-  /* Send command reply */
-  silc_server_command_send_status_reply(cmd, SILC_COMMAND_INVITE,
-                                       SILC_STATUS_OK);
+  SILC_SERVER_COMMAND_CHECK_ARGC(SILC_COMMAND_IDENTIFY, cmd, 1, 3328);
 
-  silc_buffer_free(sidp);
+  if (cmd->sock->type == SILC_SOCKET_TYPE_CLIENT)
+    ret = silc_server_command_identify_from_client(cmd);
+  else if ((cmd->sock->type == SILC_SOCKET_TYPE_SERVER) |
+          (cmd->sock->type == SILC_SOCKET_TYPE_ROUTER))
+    ret = silc_server_command_identify_from_server(cmd);
 
- out:
-  silc_server_command_free(cmd);
+  if (!ret)
+    silc_server_command_free(cmd);
 }
 
-/* Quits connection to client. This gets called if client won't
-   close the connection even when it has issued QUIT command. */
+/* Checks string for bad characters and returns TRUE if they are found. */
 
-SILC_TASK_CALLBACK(silc_server_command_quit_cb)
+static int silc_server_command_bad_chars(char *nick)
 {
-  SilcServer server = (SilcServer)context;
-  SilcSocketConnection sock = server->sockets[fd];
-
-  /* Free all client specific data, such as client entry and entires
-     on channels this client may be on. */
-  silc_server_free_sock_user_data(server, sock);
+  if (strchr(nick, '\\')) return TRUE;
+  if (strchr(nick, '\"')) return TRUE;
+  if (strchr(nick, '´')) return TRUE;
+  if (strchr(nick, '`')) return TRUE;
+  if (strchr(nick, '\'')) return TRUE;
+  if (strchr(nick, '*')) return TRUE;
+  if (strchr(nick, '/')) return TRUE;
+  if (strchr(nick, '@')) return TRUE;
 
-  /* Close the connection on our side */
-  silc_server_close_connection(server, sock);
+  return FALSE;
 }
 
-/* Quits SILC session. This is the normal way to disconnect client. */
-SILC_SERVER_CMD_FUNC(quit)
+/* 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. */
+
+SILC_SERVER_CMD_FUNC(nick)
 {
   SilcServerCommandContext cmd = (SilcServerCommandContext)context;
+  SilcClientEntry client = (SilcClientEntry)cmd->sock->user_data;
   SilcServer server = cmd->server;
-  SilcSocketConnection sock = cmd->sock;
+  SilcBuffer packet, nidp, oidp;
+  SilcClientID *new_id;
+  char *nick;
+  unsigned short ident = silc_command_get_ident(cmd->payload);
 
-  SILC_LOG_DEBUG(("Start"));
+  if (cmd->sock->type != SILC_SOCKET_TYPE_CLIENT)
+    goto out;
 
-  /* We quit the connection with little timeout */
-  silc_task_register(server->timeout_queue, sock->sock,
-                    silc_server_command_quit_cb, server,
-                    0, 300000, SILC_TASK_TIMEOUT, SILC_TASK_PRI_LOW);
+  SILC_SERVER_COMMAND_CHECK_ARGC(SILC_COMMAND_NICK, cmd, 1, 1);
 
-  silc_server_command_free(cmd);
-}
+  /* Check nickname */
+  nick = silc_argument_get_arg_type(cmd->args, 1, NULL);
+  if (silc_server_command_bad_chars(nick) == TRUE) {
+    silc_server_command_send_status_reply(cmd, SILC_COMMAND_NICK,
+                                         SILC_STATUS_ERR_BAD_NICKNAME);
+    goto out;
+  }
 
-SILC_SERVER_CMD_FUNC(kill)
-{
-}
+  if (strlen(nick) > 128)
+    nick[127] = '\0';
 
-/* Server side of command INFO. This sends information about us to 
-   the client. If client requested specific server we will send the 
-   command to that server. */
+  /* Create new Client ID */
+  silc_id_create_client_id(cmd->server->id, cmd->server->rng, 
+                          cmd->server->md5hash, nick,
+                          &new_id);
 
-SILC_SERVER_CMD_FUNC(info)
-{
-  SilcServerCommandContext cmd = (SilcServerCommandContext)context;
-  SilcServer server = cmd->server;
-  SilcBuffer packet, idp;
-  char info_string[256], *dest_server;
+  /* Send notify about nickname change to our router. We send the new
+     ID and ask to replace it with the old one. If we are router the
+     packet is broadcasted. Send NICK_CHANGE notify. */
+  if (!server->standalone)
+    silc_server_send_notify_nick_change(server, server->router->connection, 
+                                       server->server_type == SILC_SERVER ? 
+                                       FALSE : TRUE, client->id,
+                                       new_id, SILC_ID_CLIENT_LEN);
 
-  SILC_SERVER_COMMAND_CHECK_ARGC(SILC_COMMAND_INFO, cmd, 1, 1);
+  /* Remove old cache entry */
+  silc_idcache_del_by_id(server->local_list->clients, SILC_ID_CLIENT, 
+                        client->id); 
 
-  /* Get server name */
-  dest_server = silc_argument_get_arg_type(cmd->args, 1, NULL);
-  if (!dest_server) {
-    silc_server_command_send_status_reply(cmd, SILC_COMMAND_INFO,
-                                         SILC_STATUS_ERR_NO_SUCH_SERVER);
-    goto out;
+  oidp = silc_id_payload_encode(client->id, SILC_ID_CLIENT);
+
+  /* Free old ID */
+  if (client->id) {
+    memset(client->id, 0, SILC_ID_CLIENT_LEN);
+    silc_free(client->id);
   }
 
-  if (!strncasecmp(dest_server, server->server_name, strlen(dest_server))) {
-    /* Send our reply */
-    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);
+  /* Save the nickname as this client is our local client */
+  if (client->nickname)
+    silc_free(client->nickname);
 
-    idp = silc_id_payload_encode(server->id, SILC_ID_SERVER);
+  client->nickname = strdup(nick);
+  client->id = new_id;
 
-    packet = silc_command_reply_payload_encode_va(SILC_COMMAND_INFO,
-                                                 SILC_STATUS_OK, 0, 2,
-                                                 2, idp->data, idp->len,
-                                                 3, info_string, 
-                                                 strlen(info_string));
-    silc_server_packet_send(server, cmd->sock, SILC_PACKET_COMMAND_REPLY, 0, 
-                           packet->data, packet->len, FALSE);
-    
-    silc_buffer_free(packet);
-    silc_buffer_free(idp);
-  } else {
-    /* Send this command to the requested server */
+  /* Update client cache */
+  silc_idcache_add(server->local_list->clients, client->nickname, 
+                  SILC_ID_CLIENT, client->id, (void *)client, TRUE, FALSE);
 
-    if (server->server_type == SILC_SERVER && !server->standalone) {
+  nidp = silc_id_payload_encode(client->id, SILC_ID_CLIENT);
 
-    }
+  /* Send NICK_CHANGE notify to the client's channels */
+  silc_server_send_notify_on_channels(server, NULL, client, 
+                                     SILC_NOTIFY_TYPE_NICK_CHANGE, 2,
+                                     oidp->data, oidp->len, 
+                                     nidp->data, nidp->len);
 
-    if (server->server_type == SILC_ROUTER) {
+  /* 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_server_packet_send(cmd->server, cmd->sock, SILC_PACKET_COMMAND_REPLY,
+                         0, packet->data, packet->len, FALSE);
 
-    }
-  }
+  silc_buffer_free(packet);
+  silc_buffer_free(nidp);
+  silc_buffer_free(oidp);
   
  out:
   silc_server_command_free(cmd);
 }
 
-SILC_SERVER_CMD_FUNC(connect)
+SILC_SERVER_CMD_FUNC(list)
 {
 }
 
-/* Server side of command PING. This just replies to the ping. */
+/* Server side of TOPIC command. Sets topic for channel and/or returns
+   current topic to client. */
 
-SILC_SERVER_CMD_FUNC(ping)
+SILC_SERVER_CMD_FUNC(topic)
 {
   SilcServerCommandContext cmd = (SilcServerCommandContext)context;
   SilcServer server = cmd->server;
-  SilcServerID *id;
-  unsigned int len;
+  SilcClientEntry client = (SilcClientEntry)cmd->sock->user_data;
+  SilcChannelID *channel_id;
+  SilcChannelEntry channel;
+  SilcChannelClientEntry chl;
+  SilcBuffer packet, idp;
   unsigned char *tmp;
+  unsigned int argc, tmp_len;
+  unsigned short ident = silc_command_get_ident(cmd->payload);
 
-  SILC_SERVER_COMMAND_CHECK_ARGC(SILC_COMMAND_INFO, cmd, 1, 2);
+  SILC_SERVER_COMMAND_CHECK_ARGC(SILC_COMMAND_TOPIC, cmd, 1, 2);
 
-  /* Get Server ID */
-  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);
+  argc = silc_argument_get_arg_num(cmd->args);
+
+  /* Get Channel ID */
+  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);
     goto out;
   }
-  id = silc_id_str2id(tmp, SILC_ID_SERVER);
-  if (!id)
-    goto out;
-
-  if (!SILC_ID_SERVER_COMPARE(id, server->id)) {
-    /* Send our reply */
-    silc_server_command_send_status_reply(cmd, SILC_COMMAND_PING,
-                                         SILC_STATUS_OK);
-  } else {
-    silc_server_command_send_status_reply(cmd, SILC_COMMAND_PING,
-                                         SILC_STATUS_ERR_NO_SUCH_SERVER);
+  channel_id = silc_id_payload_parse_id(tmp, tmp_len);
+  if (!channel_id) {
+    silc_server_command_send_status_reply(cmd, SILC_COMMAND_TOPIC,
+                                         SILC_STATUS_ERR_NO_CHANNEL_ID);
     goto out;
   }
 
-  silc_free(id);
-
- out:
-  silc_server_command_free(cmd);
-}
+  /* Check whether the channel exists */
+  channel = silc_idlist_find_channel_by_id(server->local_list, 
+                                          channel_id, NULL);
+  if (!channel) {
+    channel = silc_idlist_find_channel_by_id(server->global_list, 
+                                            channel_id, NULL);
+    if (!channel) {
+      silc_server_command_send_status_reply(cmd, SILC_COMMAND_TOPIC,
+                                           SILC_STATUS_ERR_NO_SUCH_CHANNEL);
+      goto out;
+    }
+  }
 
-SILC_SERVER_CMD_FUNC(oper)
-{
-}
+  if (argc > 1) {
+    /* Get the 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);
+      goto out;
+    }
 
-typedef struct {
-  char *channel_name;
-  char *nickname;
-  char *username;
-  char *hostname;
-  SilcChannelEntry channel;
-  SilcServer server;
-  SilcClientEntry client;
-} JoinInternalContext;
+    if (strlen(tmp) > 256) {
+      silc_server_command_send_status_reply(cmd, SILC_COMMAND_TOPIC,
+                                           SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
+      goto out;
+    }
 
-SILC_TASK_CALLBACK(silc_server_command_join_notify)
-{
-  JoinInternalContext *ctx = (JoinInternalContext *)context;
-
-  if (ctx->channel->key && ctx->channel->key_len) {
-    SilcBuffer clidp;
-
-    clidp = silc_id_payload_encode(ctx->client->id, SILC_ID_CLIENT);
-
-    silc_server_send_notify_to_channel(ctx->server, ctx->channel, FALSE,
-                                      SILC_NOTIFY_TYPE_JOIN, 1,
-                                      clidp->data, clidp->len);
-#if 0
-    /* Send NEW_CHANNEL_USER packet to primary route */
-    silc_server_send_new_channel_user(server, server->router->connection,
-                                     server->server_type == SILC_SERVER ?
-                                     FALSE : TRUE,
-                                     channel->id, SILC_ID_CHANNEL_LEN,
-                                     client->id, SILC_ID_CLIENT_LEN);
-#endif
-
-    /* Send NAMES command reply to the joined channel so the user sees who
-       is currently on the channel. */
-    silc_server_command_send_names(ctx->server, ctx->client->connection, 
-                                  ctx->channel);
-
-    silc_buffer_free(clidp);
-    silc_free(ctx);
-  } else {
-    silc_task_register(ctx->server->timeout_queue, fd,
-                      silc_server_command_join_notify, context,
-                      0, 200000, SILC_TASK_TIMEOUT, SILC_TASK_PRI_LOW);
-  }
-}
+    /* See whether has rights to change topic */
+    silc_list_start(channel->user_list);
+    while ((chl = silc_list_get(channel->user_list)) != SILC_LIST_END)
+      if (chl->client == client)
+       break;
 
-/* Assembles NAMES command and executes it. This is called when client
-   joins to a channel and we wan't to send NAMES command reply to the 
-   client. */
+    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;
+      }
+    }
 
-void silc_server_command_send_names(SilcServer server,
-                                   SilcSocketConnection sock,
-                                   SilcChannelEntry channel)
-{
-  SilcServerCommandContext cmd;
-  SilcBuffer buffer, idp;
-
-  idp = silc_id_payload_encode(channel->id, SILC_ID_CHANNEL);
-  buffer = silc_command_payload_encode_va(SILC_COMMAND_NAMES, 0, 1,
-                                         1, idp->data, idp->len);
-
-  cmd = silc_calloc(1, sizeof(*cmd));
-  cmd->payload = silc_command_payload_parse(buffer);
-  cmd->args = silc_command_get_args(cmd->payload);
-  cmd->server = server;
-  cmd->sock = sock;
-  cmd->pending = FALSE;
-
-  silc_server_command_names((void *)cmd);
-  silc_free(buffer);
-  silc_free(idp);
-}
+    /* Set the topic for channel */
+    if (channel->topic)
+      silc_free(channel->topic);
+    channel->topic = strdup(tmp);
 
-/* Internal routine that is called after router has replied to server's
-   JOIN command it forwarded to the router. The route has joined and possibly
-   creaetd the channel. This function adds the client to the channel's user
-   list. */
+    /* Send TOPIC_SET notify type to the network */
+    if (!server->standalone)
+      silc_server_send_notify_topic_set(server, server->router->connection,
+                                       server->server_type == SILC_ROUTER ?
+                                       TRUE : FALSE, channel, client->id,
+                                       SILC_ID_CLIENT_LEN, channel->topic);
 
-SILC_SERVER_CMD_FUNC(add_to_channel)
-{
-  SilcServerCommandContext cmd = (SilcServerCommandContext)context;
-  SilcServer server = cmd->server;
-  SilcClientEntry client;
-  SilcChannelEntry channel;
-  SilcChannelClientEntry chl;
-  char *channel_name;
+    idp = silc_id_payload_encode(client->id, SILC_ID_CLIENT);
 
-  /* Get channel name */
-  channel_name = silc_argument_get_arg_type(cmd->args, 1, NULL);
+    /* Send notify about topic change to all clients on the channel */
+    silc_server_send_notify_to_channel(server, NULL, channel, TRUE,
+                                      SILC_NOTIFY_TYPE_TOPIC_SET, 2,
+                                      idp->data, idp->len,
+                                      channel->topic, strlen(channel->topic));
+    silc_buffer_free(idp);
+  }
 
-  /* Get client entry */
-  client = (SilcClientEntry)cmd->sock->user_data;
+  /* Send the topic to client as reply packet */
+  idp = silc_id_payload_encode(channel_id, SILC_ID_CHANNEL);
+  if (channel->topic)
+    packet = silc_command_reply_payload_encode_va(SILC_COMMAND_TOPIC, 
+                                                 SILC_STATUS_OK, ident, 2, 
+                                                 2, idp->data, idp->len,
+                                                 3, channel->topic, 
+                                                 strlen(channel->topic));
+  else
+    packet = silc_command_reply_payload_encode_va(SILC_COMMAND_TOPIC, 
+                                                 SILC_STATUS_OK, ident, 1, 
+                                                 2, idp->data, idp->len);
+  silc_server_packet_send(cmd->server, cmd->sock, SILC_PACKET_COMMAND_REPLY,
+                         0, packet->data, packet->len, FALSE);
 
-  /* Get channel entry */
-  channel = silc_idlist_find_channel_by_name(server->local_list, 
-                                            channel_name, NULL);
-  if (channel) {
-    /* Join the client to the channel by adding it to channel's user list.
-       Add also the channel to client entry's channels list for fast cross-
-       referencing. */
-    chl = silc_calloc(1, sizeof(*chl));
-    //chl->mode = SILC_CHANNEL_UMODE_CHANOP | SILC_CHANNEL_UMODE_CHANFO;
-    chl->client = client;
-    chl->channel = channel;
-    silc_list_add(channel->user_list, chl);
-    silc_list_add(client->channels, chl);
-  }
+  silc_buffer_free(packet);
+  silc_buffer_free(idp);
+  silc_free(channel_id);
 
+ 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. */
+/* Server side of INVITE command. Invites some client to join some channel. */
 
-static void silc_server_command_join_channel(SilcServer server, 
-                                            SilcServerCommandContext cmd,
-                                            SilcChannelEntry channel,
-                                            SilcClientID *client_id,
-                                            int created,
-                                            unsigned int umode)
+SILC_SERVER_CMD_FUNC(invite)
 {
-  SilcSocketConnection sock = cmd->sock;
+  SilcServerCommandContext cmd = (SilcServerCommandContext)context;
+  SilcServer server = cmd->server;
+  SilcSocketConnection sock = cmd->sock, dest_sock;
+  SilcClientEntry sender, dest;
+  SilcClientID *dest_id;
+  SilcChannelEntry channel;
+  SilcChannelID *channel_id;
+  SilcBuffer sidp;
   unsigned char *tmp;
-  unsigned int tmp_len;
-  unsigned char *passphrase = NULL, mode[4], tmp2[4];
-  SilcClientEntry client;
-  SilcChannelClientEntry chl;
-  SilcBuffer reply, chidp, clidp, keyp;
-  unsigned short ident = silc_command_get_ident(cmd->payload);
-
-  SILC_LOG_DEBUG(("Start"));
+  unsigned int len;
 
-  if (!channel)
-    return;
+  SILC_SERVER_COMMAND_CHECK_ARGC(SILC_COMMAND_INVITE, cmd, 1, 2);
 
-  /* 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);
+  /* Get destination ID */
+  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_CLIENT_ID);
+    goto out;
   }
-  
-  /*
-   * Check channel modes
-   */
-
-  /* Check invite list if channel is invite-only channel */
-  if (channel->mode & SILC_CHANNEL_MODE_INVITE) {
-    if (channel->mode & SILC_CHANNEL_MODE_INVITE_LIST) {
-      /* Invite list is specified. Check whether client is invited in the
-        list. If not, then check whether it has been invited otherwise. */
-
-    } else {
-      /* XXX client must be invited to be able to join the channel */
-    }
+  dest_id = silc_id_payload_parse_id(tmp, len);
+  if (!dest_id) {
+    silc_server_command_send_status_reply(cmd, SILC_COMMAND_INVITE,
+                                         SILC_STATUS_ERR_NO_CLIENT_ID);
+    goto out;
   }
 
-  /* Check ban list if set */
-  if (channel->mode & SILC_CHANNEL_MODE_BAN) {
-
+  /* Get Channel ID */
+  tmp = silc_argument_get_arg_type(cmd->args, 2, &len);
+  if (!tmp) {
+    silc_server_command_send_status_reply(cmd, SILC_COMMAND_INVITE,
+                                         SILC_STATUS_ERR_NO_CHANNEL_ID);
+    goto out;
+  }
+  channel_id = silc_id_payload_parse_id(tmp, len);
+  if (!channel_id) {
+    silc_server_command_send_status_reply(cmd, SILC_COMMAND_INVITE,
+                                         SILC_STATUS_ERR_NO_CHANNEL_ID);
+    goto out;
   }
 
-  /* Check the channel passphrase if set. */
-  if (channel->mode & SILC_CHANNEL_MODE_PASSPHRASE) {
-    if (!passphrase || memcmp(channel->mode_data.passphrase, passphrase,
-                             strlen(channel->mode_data.passphrase))) {
-      silc_server_command_send_status_reply(cmd, SILC_COMMAND_JOIN,
-                                           SILC_STATUS_ERR_BAD_PASSWORD);
+  /* Check whether the channel exists */
+  channel = silc_idlist_find_channel_by_id(server->local_list, 
+                                          channel_id, NULL);
+  if (!channel) {
+    channel = silc_idlist_find_channel_by_id(server->global_list, 
+                                            channel_id, NULL);
+    if (!channel) {
+      silc_server_command_send_status_reply(cmd, SILC_COMMAND_INVITE,
+                                           SILC_STATUS_ERR_NO_SUCH_CHANNEL);
       goto out;
     }
   }
 
-  /* Check user count limit if set. */
-  if (channel->mode & SILC_CHANNEL_MODE_ULIMIT) {
-    if (silc_list_count(channel->user_list) + 1 > 
-       channel->mode_data.user_limit) {
-      silc_server_command_send_status_reply(cmd, SILC_COMMAND_JOIN,
-                                           SILC_STATUS_ERR_CHANNEL_IS_FULL);
-      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)) {
+    silc_server_command_send_status_reply(cmd, SILC_COMMAND_INVITE,
+                                         SILC_STATUS_ERR_NOT_ON_CHANNEL);
+    goto out;
   }
 
-  /*
-   * Client is allowed to join to the channel. Make it happen.
-   */
+  /* 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) {
+    SilcChannelClientEntry chl;
 
-  /* Get the client entry */
-  if (cmd->sock->type == SILC_SOCKET_TYPE_CLIENT) {
-    client = (SilcClientEntry)sock->user_data;
-  } else {
-    client = silc_idlist_find_client_by_id(server->local_list, client_id, 
-                                          NULL);
-    if (!client) {
-      /* XXX actually this is useless since router finds always cell's
-        local clients from its local lists. */
-      client = silc_idlist_find_client_by_id(server->global_list, client_id, 
-                                            NULL);
-      if (!client)
-       goto out;
-    }
+    silc_list_start(channel->user_list);
+    while ((chl = silc_list_get(channel->user_list)) != SILC_LIST_END)
+      if (chl->client == sender) {
+       if (chl->mode == SILC_CHANNEL_UMODE_NONE) {
+         silc_server_command_send_status_reply(cmd, SILC_COMMAND_INVITE,
+                                       SILC_STATUS_ERR_NO_CHANNEL_PRIV);
+         goto out;
+       }
+       break;
+      }
   }
 
-  /* Check whether the client already is on the channel */
-  if (silc_server_client_on_channel(client, channel)) {
-    silc_server_command_send_status_reply(cmd, SILC_COMMAND_JOIN,
+  /* Find the connection data for the destination. If it is local we will
+     send it directly otherwise we will send it to router for routing. */
+  dest = silc_idlist_find_client_by_id(server->local_list, dest_id, NULL);
+  if (dest)
+    dest_sock = (SilcSocketConnection)dest->connection;
+  else
+    dest_sock = silc_server_route_get(server, dest_id, SILC_ID_CLIENT);
+
+  /* Check whether the requested client is already on the channel. */
+  /* XXX if we are normal server we don't know about global clients on
+     the channel thus we must request it (USERS command), check from
+     local cache as well. */
+  if (silc_server_client_on_channel(dest, channel)) {
+    silc_server_command_send_status_reply(cmd, SILC_COMMAND_INVITE,
                                          SILC_STATUS_ERR_USER_ON_CHANNEL);
     goto out;
   }
 
-  /* Generate new channel key as protocol dictates */
-  if (!created || !channel->channel_key)
-    silc_server_create_channel_key(server, channel, 0);
+  sidp = silc_id_payload_encode(sender->id, SILC_ID_CLIENT);
 
-  /* Send the channel key. This is broadcasted to the channel but is not
-     sent to the client who is joining to the channel. */
-  silc_server_send_channel_key(server, channel, 
-                              server->server_type == SILC_ROUTER ? 
-                              FALSE : server->standalone);
+  /* Send notify to the client that is invited to the channel */
+  silc_server_send_notify_dest(server, dest_sock, FALSE, dest_id, 
+                              SILC_ID_CLIENT,
+                              SILC_NOTIFY_TYPE_INVITE, 2, 
+                              sidp->data, sidp->len, tmp, len);
 
-  /* Join the client to the channel by adding it to channel's user list.
-     Add also the channel to client entry's channels list for fast cross-
-     referencing. */
-  chl = silc_calloc(1, sizeof(*chl));
-  chl->mode = umode;
-  chl->client = client;
-  chl->channel = channel;
-  silc_list_add(channel->user_list, chl);
-  silc_list_add(client->channels, chl);
+  /* Send command reply */
+  silc_server_command_send_status_reply(cmd, SILC_COMMAND_INVITE,
+                                       SILC_STATUS_OK);
 
-  /* Encode Client ID Payload of the original client who wants to join */
-  clidp = silc_id_payload_encode(client->id, SILC_ID_CLIENT);
+  silc_buffer_free(sidp);
 
-  /* Encode command reply packet */
-  chidp = silc_id_payload_encode(channel->id, SILC_ID_CHANNEL);
-  SILC_PUT32_MSB(channel->mode, mode);
-  SILC_PUT32_MSB(created, tmp2);
-  tmp = silc_id_id2str(channel->id, SILC_ID_CHANNEL);
-  keyp = silc_channel_key_payload_encode(SILC_ID_CHANNEL_LEN, tmp, 
-                                        SILC_ID_CHANNEL_LEN,
-                                        channel->channel_key->cipher->name,
-                                        channel->key_len / 8, channel->key);
-  silc_free(tmp);
-  if (!channel->topic) {
-    reply = 
-      silc_command_reply_payload_encode_va(SILC_COMMAND_JOIN,
-                                          SILC_STATUS_OK, ident, 5,
-                                          2, channel->channel_name,
-                                          strlen(channel->channel_name),
-                                          3, chidp->data, chidp->len,
-                                          4, mode, 4,
-                                          5, tmp2, 4,
-                                          6, keyp->data, keyp->len);
-  } else {
-    reply = 
-      silc_command_reply_payload_encode_va(SILC_COMMAND_JOIN,
-                                          SILC_STATUS_OK, ident, 6, 
-                                          2, channel->channel_name, 
-                                          strlen(channel->channel_name),
-                                          3, chidp->data, chidp->len,
-                                          4, mode, 4,
-                                          5, tmp2, 4,
-                                          6, keyp->data, keyp->len,
-                                          8, channel->topic, 
-                                          strlen(channel->topic));
-  }
+ out:
+  silc_server_command_free(cmd);
+}
 
-  /* Send command reply */
-  silc_server_packet_send(server, sock, SILC_PACKET_COMMAND_REPLY, 0, 
-                         reply->data, reply->len, FALSE);
+typedef struct {
+  SilcServer server;
+  SilcSocketConnection sock;
+  char *signoff;
+} *QuitInternal;
 
-  if (!cmd->pending)
-    /* Send JOIN notify to locally connected clients on the channel */
-    silc_server_send_notify_to_channel(server, channel, FALSE,
-                                      SILC_NOTIFY_TYPE_JOIN, 1,
-                                      clidp->data, clidp->len);
-
-  /* Send NEW_CHANNEL_USER packet to our primary router */
-  if (!cmd->pending && !server->standalone)
-    silc_server_send_new_channel_user(server, server->router->connection,
-                                     server->server_type == SILC_SERVER ?
-                                     FALSE : TRUE,
-                                     channel->id, SILC_ID_CHANNEL_LEN,
-                                     client->id, SILC_ID_CLIENT_LEN);
-
-  /* Send NAMES command reply to the joined channel so the user sees who
-     is currently on the channel. */
-  silc_server_command_send_names(server, sock, channel);
+/* Quits connection to client. This gets called if client won't
+   close the connection even when it has issued QUIT command. */
 
-  /*
+SILC_TASK_CALLBACK(silc_server_command_quit_cb)
+{
+  QuitInternal q = (QuitInternal)context;
 
-    FAQ:
+  /* Free all client specific data, such as client entry and entires
+     on channels this client may be on. */
+  silc_server_free_client_data(q->server, q->sock, q->sock->user_data,
+                              TRUE, q->signoff);
+  q->sock->user_data = NULL;
 
-   * Kuinka NAMES komento händlätään serverissä kun router lähettää sen
-   serverille joka on lähettäny sille clientin puolesta JOIN komennon?
-   
-   R: Serverin pitää ymmärtää NAMES comman replyjä.
+  /* Close the connection on our side */
+  silc_server_close_connection(q->server, q->sock);
 
-  */
+  silc_free(q->signoff);
+  silc_free(q);
+}
 
-  silc_buffer_free(reply);
-  silc_buffer_free(clidp);
-  silc_buffer_free(chidp);
-  silc_buffer_free(keyp);
+/* Quits SILC session. This is the normal way to disconnect client. */
+SILC_SERVER_CMD_FUNC(quit)
+{
+  SilcServerCommandContext cmd = (SilcServerCommandContext)context;
+  SilcServer server = cmd->server;
+  SilcSocketConnection sock = cmd->sock;
+  QuitInternal q;
+  unsigned char *tmp = NULL;
+  unsigned int len = 0;
+
+  SILC_SERVER_COMMAND_CHECK_ARGC(SILC_COMMAND_QUIT, cmd, 0, 1);
+
+  if (cmd->sock->type != SILC_SOCKET_TYPE_CLIENT)
+    goto out;
+
+  /* Get destination ID */
+  tmp = silc_argument_get_arg_type(cmd->args, 1, &len);
+  if (len > 128)
+    tmp = NULL;
+
+  q = silc_calloc(1, sizeof(*q));
+  q->server = server;
+  q->sock = sock;
+  q->signoff = tmp ? strdup(tmp) : NULL;
+
+  /* We quit the connection with little timeout */
+  silc_task_register(server->timeout_queue, sock->sock,
+                    silc_server_command_quit_cb, (void *)q,
+                    0, 200000, SILC_TASK_TIMEOUT, SILC_TASK_PRI_LOW);
 
  out:
-  if (passphrase)
-    silc_free(passphrase);
+  silc_server_command_free(cmd);
 }
 
-/* Server side of command JOIN. Joins client into requested channel. If 
-   the channel does not exist it will be created. */
+/* Server side of command KILL. This command is used by router operator
+   to remove an client from the SILC Network temporarily. */
 
-SILC_SERVER_CMD_FUNC(join)
+SILC_SERVER_CMD_FUNC(kill)
 {
   SilcServerCommandContext cmd = (SilcServerCommandContext)context;
   SilcServer server = cmd->server;
-  int tmp_len;
-  char *tmp, *channel_name = NULL, *cipher = NULL;
-  SilcChannelEntry channel;
-  unsigned int umode = 0;
-  int created = FALSE;
+  SilcClientEntry client = (SilcClientEntry)cmd->sock->user_data;
+  SilcClientEntry remote_client;
   SilcClientID *client_id;
+  unsigned char *tmp, *comment;
+  unsigned int tmp_len, tmp_len2;
 
-  SILC_SERVER_COMMAND_CHECK_ARGC(SILC_COMMAND_JOIN, cmd, 1, 4);
+  SILC_SERVER_COMMAND_CHECK_ARGC(SILC_COMMAND_KILL, cmd, 1, 2);
 
-  /* Get channel name */
-  tmp = silc_argument_get_arg_type(cmd->args, 1, &tmp_len);
-  if (!tmp) {
-    silc_server_command_send_status_reply(cmd, SILC_COMMAND_JOIN,
-                                         SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
+  if (!client || cmd->sock->type != SILC_SOCKET_TYPE_CLIENT)
+    goto out;
+
+  /* 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);
     goto out;
   }
-  channel_name = tmp;
 
-  if (silc_server_command_bad_chars(channel_name) == TRUE) {
-    silc_server_command_send_status_reply(cmd, SILC_COMMAND_JOIN,
-                                         SILC_STATUS_ERR_BAD_CHANNEL);
-    silc_free(channel_name);
+  /* 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);
     goto out;
   }
 
-  /* Get Client ID of the client who is joining to the channel */
-  tmp = silc_argument_get_arg_type(cmd->args, 2, &tmp_len);
+  /* 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_JOIN,
+    silc_server_command_send_status_reply(cmd, SILC_COMMAND_KILL,
                                          SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
     goto out;
   }
   client_id = silc_id_payload_parse_id(tmp, tmp_len);
+  if (!client_id) {
+    silc_server_command_send_status_reply(cmd, SILC_COMMAND_KILL,
+                                         SILC_STATUS_ERR_NO_SUCH_CLIENT_ID);
+    goto out;
+  }
 
-  /* Get cipher name */
-  cipher = silc_argument_get_arg_type(cmd->args, 4, NULL);
-
-  /* See if the channel exists */
-  channel = silc_idlist_find_channel_by_name(server->local_list, 
-                                            channel_name, NULL);
-
-  if (cmd->sock->type == SILC_SOCKET_TYPE_CLIENT) {
-    /* If this is coming from client the Client ID in the command packet must
-       be same as the client's ID. */
-    if (cmd->sock->type == SILC_SOCKET_TYPE_CLIENT) {
-      SilcClientEntry entry = (SilcClientEntry)cmd->sock->user_data;
-      if (SILC_ID_CLIENT_COMPARE(entry->id, client_id)) {
-       silc_server_command_send_status_reply(cmd, SILC_COMMAND_JOIN,
-                                       SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
-       goto out;
-      }
+  /* Get the client entry */
+  remote_client = silc_idlist_find_client_by_id(server->local_list, 
+                                               client_id, NULL);
+  if (!remote_client) {
+    remote_client = silc_idlist_find_client_by_id(server->global_list, 
+                                                 client_id, NULL);
+    if (!remote_client) {
+      silc_server_command_send_status_reply(cmd, SILC_COMMAND_KILL,
+                                           SILC_STATUS_ERR_NO_SUCH_CLIENT_ID);
+      goto out;
     }
+  }
 
-    if (!channel) {
-      /* Channel not found */
-
-      /* If we are standalone server we don't have a router, we just create 
-        the channel by ourselves. */
-      if (server->standalone) {
-       channel = silc_server_create_new_channel(server, server->id, cipher, 
-                                                channel_name);
-       umode = (SILC_CHANNEL_UMODE_CHANOP | SILC_CHANNEL_UMODE_CHANFO);
-       created = TRUE;
+  /* Get comment */
+  comment = silc_argument_get_arg_type(cmd->args, 2, &tmp_len2);
+  if (tmp_len2 > 128)
+    comment = NULL;
 
-      } else {
+  /* Send reply to the sender */
+  silc_server_command_send_status_reply(cmd, SILC_COMMAND_KILL,
+                                       SILC_STATUS_OK);
 
-       /* The channel does not exist on our server. If we are normal server 
-          we will send JOIN command to our router which will handle the
-          joining procedure (either creates the channel if it doesn't exist 
-          or joins the client to it). */
-       if (server->server_type == SILC_SERVER) {
-         SilcBuffer tmpbuf;
-         unsigned short old_ident;
-         
-         old_ident = silc_command_get_ident(cmd->payload);
-         silc_command_set_ident(cmd->payload, silc_rng_get_rn16(server->rng));
-         tmpbuf = silc_command_payload_encode_payload(cmd->payload);
-         
-         /* Send JOIN command to our router */
-         silc_server_packet_send(server, (SilcSocketConnection)
-                                 server->router->connection,
-                                 SILC_PACKET_COMMAND, cmd->packet->flags,
-                                 tmpbuf->data, tmpbuf->len, TRUE);
-         
-         /* Reprocess this packet after received reply from router */
-         silc_server_command_pending(server, SILC_COMMAND_JOIN, 
-                                     silc_command_get_ident(cmd->payload),
-                                     silc_server_command_join, context);
-         cmd->pending = TRUE;
-         return;
-       }
-       
-       /* We are router and the channel does not seem exist so we will check
-          our global list as well for the channel. */
-       channel = silc_idlist_find_channel_by_name(server->global_list, 
-                                                  channel_name, NULL);
-       if (!channel) {
-         /* Channel really does not exist, create it */
-         channel = silc_server_create_new_channel(server, server->id, cipher, 
-                                                  channel_name);
-         umode = (SILC_CHANNEL_UMODE_CHANOP | SILC_CHANNEL_UMODE_CHANFO);
-         created = TRUE;
-       }
-      }
-    }
-  } else {
-    if (!channel) {
-      /* Channel not found */
+  /* 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. */
 
-      /* If the command came from router and/or we are normal server then
-        something went wrong with the joining as the channel was not found.
-        We can't do anything else but ignore this. */
-      if (cmd->sock->type == SILC_SOCKET_TYPE_ROUTER ||
-         server->server_type == SILC_SERVER)
-       goto out;
-      
-      /* We are router and the channel does not seem exist so we will check
-        our global list as well for the channel. */
-      channel = silc_idlist_find_channel_by_name(server->global_list, 
-                                                channel_name, NULL);
-      if (!channel) {
-       /* Channel really does not exist, create it */
-       channel = silc_server_create_new_channel(server, server->id, cipher, 
-                                                channel_name);
-       umode = (SILC_CHANNEL_UMODE_CHANOP | SILC_CHANNEL_UMODE_CHANFO);
-       created = TRUE;
-      }
-    }
-  }
+  /* 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);
 
-  /* If the channel does not have global users and is also empty it means the
-     channel was created globally (by our router) and the client will be the
-     channel founder and operator. */
-  if (!channel->global_users && silc_list_count(channel->user_list) == 0) {
-    umode = (SILC_CHANNEL_UMODE_CHANOP | SILC_CHANNEL_UMODE_CHANFO);
-    created = TRUE;            /* Created globally by our router */
+  /* Send KILLED notify to primary route */
+  if (!server->standalone)
+    silc_server_send_notify_killed(server, server->router->connection, TRUE,
+                                  remote_client->id, SILC_ID_CLIENT_LEN,
+                                  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, SILC_ID_CLIENT_LEN,
+                                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->data.registered && 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);
   }
 
-  /* Join to the channel */
-  silc_server_command_join_channel(server, cmd, channel, client_id,
-                                  created, umode);
-
-  silc_free(client_id);
-
  out:
   silc_server_command_free(cmd);
 }
 
-/* Server side of command MOTD. Sends server's current "message of the
-   day" to the client. */
+/* Server side of command INFO. This sends information about us to 
+   the client. If client requested specific server we will send the 
+   command to that server. */
 
-SILC_SERVER_CMD_FUNC(motd)
+SILC_SERVER_CMD_FUNC(info)
 {
   SilcServerCommandContext cmd = (SilcServerCommandContext)context;
   SilcServer server = cmd->server;
-  char *motd;
-  int motd_len;
-  
-  SILC_SERVER_COMMAND_CHECK_ARGC(SILC_COMMAND_MOTD, cmd, 1, 2);
-
-  /* XXX show currently only our motd */
-
-  if (server->config && server->config->motd && 
-      server->config->motd->motd_file) {
+  SilcBuffer packet, idp;
+  char info_string[256], *dest_server;
 
-    /* Send motd */
-    motd = silc_file_read(server->config->motd->motd_file, &motd_len);
-    if (!motd)
-      goto out;
+  SILC_SERVER_COMMAND_CHECK_ARGC(SILC_COMMAND_INFO, cmd, 1, 1);
 
-    motd[motd_len] = 0;
-    silc_server_command_send_status_data(cmd, SILC_COMMAND_MOTD,
-                                        SILC_STATUS_OK,
-                                        2, motd, motd_len);
+  /* Get server name */
+  dest_server = silc_argument_get_arg_type(cmd->args, 1, NULL);
+  if (!dest_server) {
+    silc_server_command_send_status_reply(cmd, SILC_COMMAND_INFO,
+                                         SILC_STATUS_ERR_NO_SUCH_SERVER);
     goto out;
-  } else {
-    /* No motd */
-    silc_server_command_send_status_reply(cmd, SILC_COMMAND_MOTD,
-                                         SILC_STATUS_OK);
   }
 
- out:
+  if (!strncasecmp(dest_server, server->server_name, strlen(dest_server))) {
+    /* Send our reply */
+    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);
+
+    idp = silc_id_payload_encode(server->id, SILC_ID_SERVER);
+
+    packet = silc_command_reply_payload_encode_va(SILC_COMMAND_INFO,
+                                                 SILC_STATUS_OK, 0, 2,
+                                                 2, idp->data, idp->len,
+                                                 3, info_string, 
+                                                 strlen(info_string));
+    silc_server_packet_send(server, cmd->sock, SILC_PACKET_COMMAND_REPLY, 0, 
+                           packet->data, packet->len, FALSE);
+    
+    silc_buffer_free(packet);
+    silc_buffer_free(idp);
+  } else {
+    /* Send this command to the requested server */
+
+    if (server->server_type == SILC_SERVER && !server->standalone) {
+
+    }
+
+    if (server->server_type == SILC_ROUTER) {
+
+    }
+  }
+  
+ out:
   silc_server_command_free(cmd);
 }
 
-SILC_SERVER_CMD_FUNC(umode)
+/* Server side of command PING. This just replies to the ping. */
+
+SILC_SERVER_CMD_FUNC(ping)
 {
+  SilcServerCommandContext cmd = (SilcServerCommandContext)context;
+  SilcServer server = cmd->server;
+  SilcServerID *id;
+  unsigned int len;
+  unsigned char *tmp;
+
+  SILC_SERVER_COMMAND_CHECK_ARGC(SILC_COMMAND_INFO, cmd, 1, 2);
+
+  /* Get Server ID */
+  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);
+    goto out;
+  }
+  id = silc_id_str2id(tmp, len, SILC_ID_SERVER);
+  if (!id)
+    goto out;
+
+  if (!SILC_ID_SERVER_COMPARE(id, server->id)) {
+    /* Send our reply */
+    silc_server_command_send_status_reply(cmd, SILC_COMMAND_PING,
+                                         SILC_STATUS_OK);
+  } else {
+    silc_server_command_send_status_reply(cmd, SILC_COMMAND_PING,
+                                         SILC_STATUS_ERR_NO_SUCH_SERVER);
+    goto out;
+  }
+
+  silc_free(id);
+
+ out:
+  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. */
+/* 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. */
 
-int silc_server_check_cmode_rights(SilcChannelEntry channel,
-                                  SilcChannelClientEntry client,
-                                  unsigned int mode)
+static void silc_server_command_join_channel(SilcServer server, 
+                                            SilcServerCommandContext cmd,
+                                            SilcChannelEntry channel,
+                                            SilcClientID *client_id,
+                                            int created,
+                                            unsigned int umode)
 {
-  int is_op = client->mode & SILC_CHANNEL_UMODE_CHANOP;
-  int is_fo = client->mode & SILC_CHANNEL_UMODE_CHANFO;
+  SilcSocketConnection sock = cmd->sock;
+  unsigned char *tmp;
+  unsigned int tmp_len, user_count;
+  unsigned char *passphrase = NULL, mode[4], tmp2[4], tmp3[4];
+  SilcClientEntry client;
+  SilcChannelClientEntry chl;
+  SilcBuffer reply, chidp, clidp, keyp, user_list, mode_list;
+  unsigned short ident = silc_command_get_ident(cmd->payload);
 
-  /* Check whether has rights to change anything */
-  if (!is_op && !is_fo)
-    return FALSE;
+  SILC_LOG_DEBUG(("Start"));
 
-  /* Check whether has rights to change everything */
-  if (is_op && is_fo)
-    return TRUE;
+  if (!channel)
+    return;
 
-  /* 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. */
+  /* Get passphrase */
+  tmp = silc_argument_get_arg_type(cmd->args, 3, &tmp_len);
+  if (tmp) {
+    passphrase = silc_calloc(tmp_len, sizeof(*passphrase));
+    memcpy(passphrase, tmp, tmp_len);
+  }
+  
+  /*
+   * Check channel modes
+   */
 
-  if (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;
+  /* Check invite list if channel is invite-only channel */
+  if (channel->mode & SILC_CHANNEL_MODE_INVITE) {
+    if (channel->mode & SILC_CHANNEL_MODE_INVITE_LIST) {
+      /* Invite list is specified. Check whether client is invited in the
+        list. If not, then check whether it has been invited otherwise. */
+
+    } else {
+      /* XXX client must be invited to be able to join the channel */
     }
   }
-  
-  if (mode & SILC_CHANNEL_MODE_PASSPHRASE) {
-    if (is_op && !is_fo)
-      return FALSE;
+
+  /* Check ban list if set */
+  if (channel->mode & SILC_CHANNEL_MODE_BAN) {
+
+  }
+
+  /* Check the channel passphrase if set. */
+  if (channel->mode & SILC_CHANNEL_MODE_PASSPHRASE) {
+    if (!passphrase || memcmp(channel->mode_data.passphrase, passphrase,
+                             strlen(channel->mode_data.passphrase))) {
+      silc_server_command_send_status_reply(cmd, SILC_COMMAND_JOIN,
+                                           SILC_STATUS_ERR_BAD_PASSWORD);
+      goto out;
+    }
+  }
+
+  /* Check user count limit if set. */
+  if (channel->mode & SILC_CHANNEL_MODE_ULIMIT) {
+    if (silc_list_count(channel->user_list) + 1 > 
+       channel->mode_data.user_limit) {
+      silc_server_command_send_status_reply(cmd, SILC_COMMAND_JOIN,
+                                           SILC_STATUS_ERR_CHANNEL_IS_FULL);
+      goto out;
+    }
+  }
+
+  /*
+   * Client is allowed to join to the channel. Make it happen.
+   */
+
+  /* Get the client entry */
+  if (cmd->sock->type == SILC_SOCKET_TYPE_CLIENT) {
+    client = (SilcClientEntry)sock->user_data;
   } else {
-    if (channel->mode & SILC_CHANNEL_MODE_PASSPHRASE) {
-      if (is_op && !is_fo)
-       return FALSE;
+    client = silc_idlist_find_client_by_id(server->local_list, client_id, 
+                                          NULL);
+    if (!client) {
+      /* XXX actually this is useless since router finds always cell's
+        local clients from its local lists. */
+      client = silc_idlist_find_client_by_id(server->global_list, client_id, 
+                                            NULL);
+      if (!client)
+       goto out;
     }
   }
 
-  if (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;
-    }
-  }
-  
-  return TRUE;
+  /* Check whether the client already is on the channel */
+  if (silc_server_client_on_channel(client, channel)) {
+    silc_server_command_send_status_reply(cmd, SILC_COMMAND_JOIN,
+                                         SILC_STATUS_ERR_USER_ON_CHANNEL);
+    goto out;
+  }
+
+  /* Generate new channel key as protocol dictates */
+  if ((!created && silc_list_count(channel->user_list) > 0) || 
+      !channel->channel_key)
+    silc_server_create_channel_key(server, channel, 0);
+
+  /* Send the channel key. This is broadcasted to the channel but is not
+     sent to the client who is joining to the channel. */
+  silc_server_send_channel_key(server, NULL, channel, 
+                              server->server_type == SILC_ROUTER ? 
+                              FALSE : !server->standalone);
+
+  /* Join the client to the channel by adding it to channel's user list.
+     Add also the channel to client entry's channels list for fast cross-
+     referencing. */
+  chl = silc_calloc(1, sizeof(*chl));
+  chl->mode = umode;
+  chl->client = client;
+  chl->channel = channel;
+  silc_list_add(channel->user_list, chl);
+  silc_list_add(client->channels, chl);
+
+  /* Get users on the channel */
+  silc_server_get_users_on_channel(server, channel, &user_list, &mode_list,
+                                  &user_count);
+
+  /* Encode Client ID Payload of the original client who wants to join */
+  clidp = silc_id_payload_encode(client->id, SILC_ID_CLIENT);
+
+  /* Encode command reply packet */
+  chidp = silc_id_payload_encode(channel->id, SILC_ID_CHANNEL);
+  SILC_PUT32_MSB(channel->mode, mode);
+  SILC_PUT32_MSB(created, tmp2);
+  SILC_PUT32_MSB(user_count, tmp3);
+  tmp = silc_id_id2str(channel->id, SILC_ID_CHANNEL);
+  keyp = silc_channel_key_payload_encode(SILC_ID_CHANNEL_LEN, tmp, 
+                                        strlen(channel->channel_key->
+                                               cipher->name),
+                                        channel->channel_key->cipher->name,
+                                        channel->key_len / 8, channel->key);
+  silc_free(tmp);
+  if (!channel->topic) {
+    reply = 
+      silc_command_reply_payload_encode_va(SILC_COMMAND_JOIN,
+                                          SILC_STATUS_OK, ident, 9,
+                                          2, channel->channel_name,
+                                          strlen(channel->channel_name),
+                                          3, chidp->data, chidp->len,
+                                          4, clidp->data, clidp->len,
+                                          5, mode, 4,
+                                          6, tmp2, 4,
+                                          7, keyp->data, keyp->len,
+                                          12, tmp3, 4,
+                                          13, user_list->data, user_list->len,
+                                          14, mode_list->data, 
+                                          mode_list->len);
+  } else {
+    reply = 
+      silc_command_reply_payload_encode_va(SILC_COMMAND_JOIN,
+                                          SILC_STATUS_OK, ident, 10, 
+                                          2, channel->channel_name, 
+                                          strlen(channel->channel_name),
+                                          3, chidp->data, chidp->len,
+                                          4, clidp->data, clidp->len,
+                                          5, mode, 4,
+                                          6, tmp2, 4,
+                                          7, keyp->data, keyp->len,
+                                          10, channel->topic, 
+                                          strlen(channel->topic),
+                                          12, tmp3, 4,
+                                          13, user_list->data, user_list->len,
+                                          14, mode_list->data, 
+                                          mode_list->len);
+  }
+
+  /* Send command reply */
+  silc_server_packet_send(server, sock, SILC_PACKET_COMMAND_REPLY, 0, 
+                         reply->data, reply->len, FALSE);
+
+  if (!cmd->pending) {
+    /* Send JOIN notify to locally connected clients on the channel */
+    silc_server_send_notify_to_channel(server, NULL, channel, FALSE,
+                                      SILC_NOTIFY_TYPE_JOIN, 2,
+                                      clidp->data, clidp->len,
+                                      chidp->data, chidp->len);
+
+    /* Send JOIN notify packet to our primary router */
+    if (!server->standalone)
+      silc_server_send_notify_join(server, server->router->connection,
+                                  server->server_type == SILC_ROUTER ?
+                                  TRUE : FALSE, channel, client->id,
+                                  SILC_ID_CLIENT_LEN);
+  }
+
+  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);
+
+ out:
+  if (passphrase)
+    silc_free(passphrase);
+}
+
+/* Server side of command JOIN. Joins client into requested channel. If 
+   the channel does not exist it will be created. */
+
+SILC_SERVER_CMD_FUNC(join)
+{
+  SilcServerCommandContext cmd = (SilcServerCommandContext)context;
+  SilcServer server = cmd->server;
+  int tmp_len;
+  char *tmp, *channel_name = NULL, *cipher, *hmac;
+  SilcChannelEntry channel;
+  unsigned int umode = 0;
+  int created = FALSE;
+  SilcClientID *client_id;
+
+  SILC_SERVER_COMMAND_CHECK_ARGC(SILC_COMMAND_JOIN, cmd, 1, 4);
+
+  /* Get channel name */
+  tmp = silc_argument_get_arg_type(cmd->args, 1, &tmp_len);
+  if (!tmp) {
+    silc_server_command_send_status_reply(cmd, SILC_COMMAND_JOIN,
+                                         SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
+    goto out;
+  }
+  channel_name = tmp;
+
+  if (strlen(channel_name) > 256)
+    channel_name[255] = '\0';
+
+  if (silc_server_command_bad_chars(channel_name) == TRUE) {
+    silc_server_command_send_status_reply(cmd, SILC_COMMAND_JOIN,
+                                         SILC_STATUS_ERR_BAD_CHANNEL);
+    silc_free(channel_name);
+    goto out;
+  }
+
+  /* Get Client ID of the client who is joining to the channel */
+  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);
+    goto out;
+  }
+  client_id = silc_id_payload_parse_id(tmp, tmp_len);
+  if (!client_id) {
+    silc_server_command_send_status_reply(cmd, SILC_COMMAND_JOIN,
+                                         SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
+    goto out;
+  }
+
+  /* Get cipher and hmac name */
+  cipher = silc_argument_get_arg_type(cmd->args, 4, NULL);
+  hmac = silc_argument_get_arg_type(cmd->args, 5, NULL);
+
+  /* See if the channel exists */
+  channel = silc_idlist_find_channel_by_name(server->local_list, 
+                                            channel_name, NULL);
+
+  if (cmd->sock->type == SILC_SOCKET_TYPE_CLIENT) {
+    /* If this is coming from client the Client ID in the command packet must
+       be same as the client's ID. */
+    if (cmd->sock->type == SILC_SOCKET_TYPE_CLIENT) {
+      SilcClientEntry entry = (SilcClientEntry)cmd->sock->user_data;
+      if (SILC_ID_CLIENT_COMPARE(entry->id, client_id)) {
+       silc_server_command_send_status_reply(cmd, SILC_COMMAND_JOIN,
+                                       SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
+       goto out;
+      }
+    }
+
+    if (!channel) {
+      /* Channel not found */
+
+      /* If we are standalone server we don't have a router, we just create 
+        the channel by ourselves. */
+      if (server->standalone) {
+       channel = silc_server_create_new_channel(server, server->id, cipher, 
+                                                hmac, channel_name, TRUE);
+       umode = (SILC_CHANNEL_UMODE_CHANOP | SILC_CHANNEL_UMODE_CHANFO);
+       created = TRUE;
+
+      } else {
+
+       /* The channel does not exist on our server. If we are normal server 
+          we will send JOIN command to our router which will handle the
+          joining procedure (either creates the channel if it doesn't exist 
+          or joins the client to it). */
+       if (server->server_type == SILC_SERVER) {
+         SilcBuffer tmpbuf;
+         unsigned short old_ident;
+         
+         old_ident = silc_command_get_ident(cmd->payload);
+         silc_command_set_ident(cmd->payload, silc_rng_get_rn16(server->rng));
+         tmpbuf = silc_command_payload_encode_payload(cmd->payload);
+         
+         /* Send JOIN command to our router */
+         silc_server_packet_send(server, (SilcSocketConnection)
+                                 server->router->connection,
+                                 SILC_PACKET_COMMAND, cmd->packet->flags,
+                                 tmpbuf->data, tmpbuf->len, TRUE);
+         
+         /* Reprocess this packet after received reply from router */
+         silc_server_command_pending(server, SILC_COMMAND_JOIN, 
+                                     silc_command_get_ident(cmd->payload),
+                                     silc_server_command_destructor,
+                                     silc_server_command_join,
+                                     silc_server_command_dup(cmd));
+         cmd->pending = TRUE;
+         return;
+       }
+       
+       /* We are router and the channel does not seem exist so we will check
+          our global list as well for the channel. */
+       channel = silc_idlist_find_channel_by_name(server->global_list, 
+                                                  channel_name, NULL);
+       if (!channel) {
+         /* Channel really does not exist, create it */
+         channel = silc_server_create_new_channel(server, server->id, cipher, 
+                                                  hmac, channel_name, TRUE);
+         umode = (SILC_CHANNEL_UMODE_CHANOP | SILC_CHANNEL_UMODE_CHANFO);
+         created = TRUE;
+       }
+      }
+    }
+  } else {
+    if (!channel) {
+      /* Channel not found */
+
+      /* If the command came from router and/or we are normal server then
+        something went wrong with the joining as the channel was not found.
+        We can't do anything else but ignore this. */
+      if (cmd->sock->type == SILC_SOCKET_TYPE_ROUTER ||
+         server->server_type == SILC_SERVER)
+       goto out;
+      
+      /* We are router and the channel does not seem exist so we will check
+        our global list as well for the channel. */
+      channel = silc_idlist_find_channel_by_name(server->global_list, 
+                                                channel_name, NULL);
+      if (!channel) {
+       /* Channel really does not exist, create it */
+       channel = silc_server_create_new_channel(server, server->id, cipher, 
+                                                hmac, channel_name, TRUE);
+       umode = (SILC_CHANNEL_UMODE_CHANOP | SILC_CHANNEL_UMODE_CHANFO);
+       created = TRUE;
+      }
+    }
+  }
+
+  /* If the channel does not have global users and is also empty it means the
+     channel was created globally (by our router) and the client will be the
+     channel founder and operator. */
+  if (!channel->global_users && silc_list_count(channel->user_list) == 0) {
+    umode = (SILC_CHANNEL_UMODE_CHANOP | SILC_CHANNEL_UMODE_CHANFO);
+    created = TRUE;            /* Created globally by our router */
+  }
+
+  /* Join to the channel */
+  silc_server_command_join_channel(server, cmd, channel, client_id,
+                                  created, umode);
+
+  silc_free(client_id);
+
+ out:
+  silc_server_command_free(cmd);
+}
+
+/* Server side of command MOTD. Sends server's current "message of the
+   day" to the client. */
+
+SILC_SERVER_CMD_FUNC(motd)
+{
+  SilcServerCommandContext cmd = (SilcServerCommandContext)context;
+  SilcServer server = cmd->server;
+  char *motd;
+  int motd_len;
+  
+  SILC_SERVER_COMMAND_CHECK_ARGC(SILC_COMMAND_MOTD, cmd, 1, 2);
+
+  /* XXX show currently only our motd */
+
+  if (server->config && server->config->motd && 
+      server->config->motd->motd_file) {
+
+    /* Send motd */
+    motd = silc_file_read(server->config->motd->motd_file, &motd_len);
+    if (!motd)
+      goto out;
+
+    motd[motd_len] = 0;
+    silc_server_command_send_status_data(cmd, SILC_COMMAND_MOTD,
+                                        SILC_STATUS_OK,
+                                        2, motd, motd_len);
+    goto out;
+  } else {
+    /* No motd */
+    silc_server_command_send_status_reply(cmd, SILC_COMMAND_MOTD,
+                                         SILC_STATUS_OK);
+  }
+
+ out:
+  silc_server_command_free(cmd);
+}
+
+/* Server side of command UMODE. Client can use this command to set/unset
+   user mode. Client actually cannot set itself to be as server/router
+   operator so this can be used only to unset the modes. */
+
+SILC_SERVER_CMD_FUNC(umode)
+{
+  SilcServerCommandContext cmd = (SilcServerCommandContext)context;
+  SilcServer server = cmd->server;
+  SilcClientEntry client = (SilcClientEntry)cmd->sock->user_data;
+  SilcBuffer packet;
+  unsigned char *tmp_mask;
+  unsigned int mask;
+  unsigned short ident = silc_command_get_ident(cmd->payload);
+
+  if (cmd->sock->type != SILC_SOCKET_TYPE_CLIENT)
+    goto out;
+
+  SILC_SERVER_COMMAND_CHECK_ARGC(SILC_COMMAND_UMODE, cmd, 2, 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;
+  }
+  SILC_GET32_MSB(mask, tmp_mask);
+
+  /* 
+   * Change the mode 
+   */
+
+  if (mask & SILC_UMODE_SERVER_OPERATOR) {
+    /* Cannot operator mode */
+    silc_server_command_send_status_reply(cmd, SILC_COMMAND_UMODE,
+                                         SILC_STATUS_ERR_PERM_DENIED);
+    goto out;
+  } else {
+    if (client->mode & SILC_UMODE_SERVER_OPERATOR)
+      /* Remove the server operator rights */
+      client->mode &= ~SILC_UMODE_SERVER_OPERATOR;
+  }
+
+  if (mask & SILC_UMODE_ROUTER_OPERATOR) {
+    /* Cannot operator mode */
+    silc_server_command_send_status_reply(cmd, SILC_COMMAND_UMODE,
+                                         SILC_STATUS_ERR_PERM_DENIED);
+    goto out;
+  } else {
+    if (client->mode & SILC_UMODE_ROUTER_OPERATOR)
+      /* Remove the router operator rights */
+      client->mode &= ~SILC_UMODE_ROUTER_OPERATOR;
+  }
+
+  /* Send UMODE change to primary router */
+  if (!server->standalone)
+    silc_server_send_notify_umode(server, server->router->connection, TRUE,
+                                 client->id, SILC_ID_CLIENT_LEN,
+                                 client->mode);
+
+  /* Send command reply to sender */
+  packet = silc_command_reply_payload_encode_va(SILC_COMMAND_UMODE,
+                                               SILC_STATUS_OK, ident, 1,
+                                               2, tmp_mask, 4);
+  silc_server_packet_send(server, cmd->sock, SILC_PACKET_COMMAND_REPLY, 0, 
+                         packet->data, packet->len, FALSE);
+  silc_buffer_free(packet);
+
+ out:
+  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,
+                                  unsigned int 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 (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 (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 (is_op && !is_fo)
+      return FALSE;
+  } else {
+    if (channel->mode & SILC_CHANNEL_MODE_CIPHER) {
+      if (is_op && !is_fo)
+       return FALSE;
+    }
+  }
+  
+  return TRUE;
+}
+
+/* Server side command of CMODE. Changes channel mode */
+
+SILC_SERVER_CMD_FUNC(cmode)
+{
+  SilcServerCommandContext cmd = (SilcServerCommandContext)context;
+  SilcServer server = cmd->server;
+  SilcClientEntry client = (SilcClientEntry)cmd->sock->user_data;
+  SilcChannelID *channel_id;
+  SilcChannelEntry channel;
+  SilcChannelClientEntry chl;
+  SilcBuffer packet, cidp;
+  unsigned char *tmp, *tmp_id, *tmp_mask;
+  unsigned int argc, mode_mask, tmp_len, tmp_len2;
+  unsigned short ident = silc_command_get_ident(cmd->payload);
+
+  SILC_LOG_DEBUG(("Start"));
+
+  argc = silc_argument_get_arg_num(cmd->args);
+  if (argc < 2) {
+    silc_server_command_send_status_reply(cmd, SILC_COMMAND_CMODE,
+                                         SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
+    goto out;
+  }
+  if (argc > 8) {
+    silc_server_command_send_status_reply(cmd, SILC_COMMAND_CMODE,
+                                         SILC_STATUS_ERR_TOO_MANY_PARAMS);
+    goto out;
+  }
+
+  /* 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);
+    goto out;
+  }
+  channel_id = silc_id_payload_parse_id(tmp_id, tmp_len2);
+  if (!channel_id) {
+    silc_server_command_send_status_reply(cmd, SILC_COMMAND_CMODE,
+                                         SILC_STATUS_ERR_NO_CHANNEL_ID);
+    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;
+  }
+  SILC_GET32_MSB(mode_mask, tmp_mask);
+
+  /* Get channel entry */
+  channel = silc_idlist_find_channel_by_id(server->local_list, 
+                                          channel_id, NULL);
+  if (!channel) {
+    channel = silc_idlist_find_channel_by_id(server->global_list, 
+                                            channel_id, NULL);
+    if (!channel) {
+      silc_server_command_send_status_reply(cmd, SILC_COMMAND_CMODE,
+                                           SILC_STATUS_ERR_NO_SUCH_CHANNEL);
+      goto out;
+    }
+  }
+
+  /* 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_CMODE,
+                                         SILC_STATUS_ERR_NOT_ON_CHANNEL);
+    goto out;
+  }
+
+  /* Get entry to the channel user list */
+  silc_list_start(channel->user_list);
+  while ((chl = silc_list_get(channel->user_list)) != SILC_LIST_END)
+    if (chl->client == client)
+      break;
+
+  /* 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);
+    goto out;
+  }
+
+  /*
+   * Check the modes. Modes that requires nothing special operation are
+   * not checked here.
+   */
+
+  if (mode_mask & SILC_CHANNEL_MODE_PRIVKEY) {
+    /* Channel uses private keys to protect traffic. Client(s) has set the
+       key locally they want to use, server does not know that key. */
+    /* Nothing interesting to do here now */
+  } else {
+    if (channel->mode & SILC_CHANNEL_MODE_PRIVKEY) {
+      /* The mode is removed and we need to generate and distribute
+        new channel key. Clients are not using private channel keys
+        anymore after this. */
+
+      /* XXX Duplicated code, make own function for this!! LEAVE uses this
+        as well */
+
+      /* Re-generate channel key */
+      silc_server_create_channel_key(server, channel, 0);
+      
+      /* Encode channel key payload to be distributed on the channel */
+      packet = 
+       silc_channel_key_payload_encode(tmp_len2, tmp_id,
+                                       strlen(channel->channel_key->
+                                              cipher->name),
+                                       channel->channel_key->cipher->name,
+                                       channel->key_len / 8, channel->key);
+      
+      /* If we are normal server then we will send it to our router.  If we
+        are router we will send it to all local servers that has clients on
+        the channel */
+      if (server->server_type == SILC_SERVER) {
+       if (!server->standalone)
+         silc_server_packet_send(server, 
+                                 cmd->server->router->connection,
+                                 SILC_PACKET_CHANNEL_KEY, 0, packet->data,
+                                 packet->len, TRUE);
+      } else {
+       
+      }
+      
+      /* Send to locally connected clients on the channel */
+      silc_server_packet_send_local_channel(server, channel, 
+                                           SILC_PACKET_CHANNEL_KEY, 0,
+                                           packet->data, packet->len, FALSE);
+      silc_buffer_free(packet);
+    }
+  }
+  
+  if (mode_mask & SILC_CHANNEL_MODE_ULIMIT) {
+    /* User limit is set on channel */
+    unsigned int 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);
+       goto out;
+      }
+    } else {
+      SILC_GET32_MSB(user_limit, tmp);
+      channel->mode_data.user_limit = user_limit;
+    }
+  } else {
+    if (channel->mode & SILC_CHANNEL_MODE_ULIMIT)
+      /* User limit mode is unset. Remove user limit */
+      channel->mode_data.user_limit = 0;
+  }
+
+  if (mode_mask & SILC_CHANNEL_MODE_PASSPHRASE) {
+    if (!(channel->mode & SILC_CHANNEL_MODE_PASSPHRASE)) {
+      /* Passphrase has been set to channel */
+      
+      /* Get the passphrase */
+      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);
+       goto out;
+      }
+
+      /* Save the passphrase */
+      channel->mode_data.passphrase = strdup(tmp);
+    }
+  } else {
+    if (channel->mode & SILC_CHANNEL_MODE_PASSPHRASE) {
+      /* Passphrase mode is unset. remove the passphrase */
+      if (channel->mode_data.passphrase) {
+       silc_free(channel->mode_data.passphrase);
+       channel->mode_data.passphrase = NULL;
+      }
+    }
+  }
+
+  if (mode_mask & SILC_CHANNEL_MODE_BAN) {
+    if (!(channel->mode & SILC_CHANNEL_MODE_BAN)) {
+      /* Ban list is specified for channel */
+
+      /* Get ban list */
+      tmp = silc_argument_get_arg_type(cmd->args, 5, NULL);
+      if (!tmp) {
+       silc_server_command_send_status_reply(cmd, SILC_COMMAND_CMODE,
+                                  SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
+       goto out;
+      }
+
+      /* XXX check that channel founder is not banned */
+
+      /* Save the ban list */
+      channel->mode_data.ban_list = strdup(tmp);
+    }
+  } else {
+    if (channel->mode & SILC_CHANNEL_MODE_BAN) {
+      /* Ban mode is unset. Remove the entire ban list */
+      if (channel->mode_data.ban_list) {
+       silc_free(channel->mode_data.ban_list);
+       channel->mode_data.ban_list = NULL;
+      }
+    }
+  }
+
+  if (mode_mask & SILC_CHANNEL_MODE_INVITE_LIST) {
+    if (!(channel->mode & SILC_CHANNEL_MODE_INVITE_LIST)) {
+      /* Invite list is specified for channel */
+
+      /* Get invite list */
+      tmp = silc_argument_get_arg_type(cmd->args, 6, NULL);
+      if (!tmp) {
+       silc_server_command_send_status_reply(cmd, SILC_COMMAND_CMODE,
+                                  SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
+       goto out;
+      }
+
+      /* Save the invite linst */
+      channel->mode_data.invite_list = strdup(tmp);
+    }
+  } else {
+    if (channel->mode & SILC_CHANNEL_MODE_INVITE_LIST) {
+      /* Invite list mode is unset. Remove the entire invite list */
+      if (channel->mode_data.invite_list) {
+       silc_free(channel->mode_data.invite_list);
+       channel->mode_data.invite_list = NULL;
+      }
+    }
+  }
+
+  if (mode_mask & SILC_CHANNEL_MODE_CIPHER) {
+    if (!(channel->mode & SILC_CHANNEL_MODE_CIPHER)) {
+      /* Cipher to use protect the traffic */
+      unsigned int key_len;
+
+      /* Get cipher */
+      tmp = silc_argument_get_arg_type(cmd->args, 8, NULL);
+      if (!tmp) {
+       silc_server_command_send_status_reply(cmd, SILC_COMMAND_CMODE,
+                                  SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
+       goto out;
+      }
+
+      /* XXX Duplicated code, make own function for this!! */
+    
+      /* Delete old cipher and allocate the new one */
+      silc_cipher_free(channel->channel_key);
+      if (!silc_cipher_alloc(tmp, &channel->channel_key)) {
+       silc_server_command_send_status_reply(cmd, SILC_COMMAND_CMODE,
+                                      SILC_STATUS_ERR_UNKNOWN_ALGORITHM);
+       goto out;
+      }
+      key_len = silc_cipher_get_key_len(channel->channel_key) / 8;
+
+      /* Re-generate channel key */
+      silc_server_create_channel_key(server, channel, key_len);
+    
+      /* Encode channel key payload to be distributed on the channel */
+      packet = 
+       silc_channel_key_payload_encode(tmp_len2, tmp_id,
+                                       strlen(channel->channel_key->
+                                              cipher->name),
+                                       channel->channel_key->cipher->name,
+                                       channel->key_len / 8, channel->key);
+    
+      /* If we are normal server then we will send it to our router.  If we
+        are router we will send it to all local servers that has clients on
+        the channel */
+      if (server->server_type == SILC_SERVER) {
+       if (!server->standalone)
+         silc_server_packet_send(server, 
+                                 cmd->server->router->connection,
+                                 SILC_PACKET_CHANNEL_KEY, 0, packet->data,
+                                 packet->len, TRUE);
+      } else {
+       
+      }
+    
+      /* Send to locally connected clients on the channel */
+      silc_server_packet_send_local_channel(server, channel, 
+                                           SILC_PACKET_CHANNEL_KEY, 0,
+                                         packet->data, packet->len, FALSE);
+      silc_buffer_free(packet);
+    }
+  } else {
+    if (channel->mode & SILC_CHANNEL_MODE_CIPHER) {
+      /* Cipher mode is unset. Remove the cipher and revert back to 
+        default cipher */
+
+      if (channel->mode_data.cipher) {
+       silc_free(channel->mode_data.cipher);
+       channel->mode_data.cipher = NULL;
+       channel->mode_data.key_len = 0;
+      }
+
+      /* Generate new cipher and key for the channel */
+
+      /* XXX Duplicated code, make own function for this!! */
+
+      /* Delete old cipher and allocate default one */
+      silc_cipher_free(channel->channel_key);
+      if (!channel->cipher)
+       silc_cipher_alloc("aes-256-cbc", &channel->channel_key);
+      else {
+       if (!silc_cipher_alloc(channel->cipher, &channel->channel_key)) {
+         silc_server_command_send_status_reply(cmd, SILC_COMMAND_CMODE,
+                                 SILC_STATUS_ERR_UNKNOWN_ALGORITHM);
+         goto out;
+       }
+      }
+
+      /* Re-generate channel key */
+      silc_server_create_channel_key(server, channel, 0);
+      
+      /* Encode channel key payload to be distributed on the channel */
+      packet = 
+       silc_channel_key_payload_encode(tmp_len2, tmp_id,
+                                       strlen(channel->channel_key->
+                                              cipher->name),
+                                       channel->channel_key->cipher->name,
+                                       channel->key_len / 8, channel->key);
+      
+      /* If we are normal server then we will send it to our router.  If we
+        are router we will send it to all local servers that has clients on
+        the channel */
+      if (server->server_type == SILC_SERVER) {
+       if (!server->standalone)
+         silc_server_packet_send(server, 
+                                 cmd->server->router->connection,
+                                 SILC_PACKET_CHANNEL_KEY, 0, packet->data,
+                                 packet->len, TRUE);
+      } else {
+       
+      }
+      
+      /* Send to locally connected clients on the channel */
+      silc_server_packet_send_local_channel(server, channel, 
+                                           SILC_PACKET_CHANNEL_KEY, 0,
+                                           packet->data, packet->len, FALSE);
+      silc_buffer_free(packet);
+    }
+  }
+
+  /* Finally, set the mode */
+  channel->mode = mode_mask;
+
+  /* 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, 2,
+                                    cidp->data, cidp->len, 
+                                    tmp_mask, tmp_len);
+
+  /* Set CMODE notify type to network */
+  if (!server->standalone)
+    silc_server_send_notify_cmode(server, server->router->connection,
+                                 server->server_type == SILC_ROUTER ? 
+                                 TRUE : FALSE, channel,
+                                 mode_mask, client->id, SILC_ID_CLIENT_LEN);
+
+  /* Send command reply to sender */
+  packet = silc_command_reply_payload_encode_va(SILC_COMMAND_CMODE,
+                                               SILC_STATUS_OK, ident, 1,
+                                               2, tmp_mask, 4);
+  silc_server_packet_send(server, cmd->sock, SILC_PACKET_COMMAND_REPLY, 0, 
+                         packet->data, packet->len, FALSE);
+    
+  silc_buffer_free(packet);
+  silc_free(channel_id);
+  silc_free(cidp);
+
+ out:
+  silc_server_command_free(cmd);
 }
 
-/* Server side command of CMODE. Changes channel mode */
+/* Server side of CUMODE command. Changes client's mode on a channel. */
 
-SILC_SERVER_CMD_FUNC(cmode)
+SILC_SERVER_CMD_FUNC(cumode)
 {
   SilcServerCommandContext cmd = (SilcServerCommandContext)context;
   SilcServer server = cmd->server;
   SilcClientEntry client = (SilcClientEntry)cmd->sock->user_data;
   SilcChannelID *channel_id;
+  SilcClientID *client_id;
   SilcChannelEntry channel;
+  SilcClientEntry target_client;
   SilcChannelClientEntry chl;
-  SilcBuffer packet, cidp;
-  unsigned char *tmp, *tmp_id, *tmp_mask;
-  unsigned int argc, mode_mask, tmp_len, tmp_len2;
+  SilcBuffer packet, idp;
+  unsigned char *tmp_id, *tmp_ch_id, *tmp_mask;
+  unsigned int target_mask, sender_mask, tmp_len, tmp_ch_len;
+  int notify = FALSE;
+  unsigned short ident = silc_command_get_ident(cmd->payload);
 
-  SILC_LOG_DEBUG(("Start"));
+  SILC_SERVER_COMMAND_CHECK_ARGC(SILC_COMMAND_CUMODE, cmd, 3, 3);
 
-  argc = silc_argument_get_arg_num(cmd->args);
-  if (argc < 2) {
-    silc_server_command_send_status_reply(cmd, SILC_COMMAND_CMODE,
-                                         SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
+  /* Get Channel ID */
+  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);
     goto out;
   }
-  if (argc > 8) {
-    silc_server_command_send_status_reply(cmd, SILC_COMMAND_CMODE,
-                                         SILC_STATUS_ERR_TOO_MANY_PARAMS);
+  channel_id = silc_id_payload_parse_id(tmp_ch_id, tmp_ch_len);
+  if (!channel_id) {
+    silc_server_command_send_status_reply(cmd, SILC_COMMAND_CUMODE,
+                                         SILC_STATUS_ERR_NO_CHANNEL_ID);
     goto out;
   }
 
-  /* 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);
+  /* Get channel entry */
+  channel = silc_idlist_find_channel_by_id(server->local_list, 
+                                          channel_id, NULL);
+  if (!channel) {
+    channel = silc_idlist_find_channel_by_id(server->global_list, 
+                                            channel_id, NULL);
+    if (!channel) {
+      silc_server_command_send_status_reply(cmd, SILC_COMMAND_CUMODE,
+                                           SILC_STATUS_ERR_NO_SUCH_CHANNEL);
+      goto out;
+    }
+  }
+
+  /* Check whether sender is on the channel */
+  if (!silc_server_client_on_channel(client, channel)) {
+    silc_server_command_send_status_reply(cmd, SILC_COMMAND_CUMODE,
+                                         SILC_STATUS_ERR_NOT_ON_CHANNEL);
     goto out;
   }
-  channel_id = silc_id_payload_parse_id(tmp_id, tmp_len2);
 
-  /* Get the channel mode mask */
-  tmp_mask = silc_argument_get_arg_type(cmd->args, 2, &tmp_len);
+  /* Check that client has rights to change other's rights */
+  silc_list_start(channel->user_list);
+  while ((chl = silc_list_get(channel->user_list)) != SILC_LIST_END) {
+    if (chl->client == client) {
+      if (!(chl->mode & SILC_CHANNEL_UMODE_CHANFO) &&
+         !(chl->mode & SILC_CHANNEL_UMODE_CHANOP)) {
+       silc_server_command_send_status_reply(cmd, SILC_COMMAND_CUMODE,
+                                             SILC_STATUS_ERR_NO_CHANNEL_PRIV);
+       goto out;
+      }
+
+      sender_mask = chl->mode;
+      break;
+    }
+  }
+  
+  /* 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_CMODE,
+    silc_server_command_send_status_reply(cmd, SILC_COMMAND_CUMODE,
                                          SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
     goto out;
   }
-  SILC_GET32_MSB(mode_mask, tmp_mask);
+  SILC_GET32_MSB(target_mask, tmp_mask);
 
-  /* Get channel entry */
-  channel = silc_idlist_find_channel_by_id(server->local_list, 
-                                          channel_id, NULL);
-  if (!channel) {
-    silc_server_command_send_status_reply(cmd, SILC_COMMAND_CMODE,
-                                         SILC_STATUS_ERR_NO_SUCH_CHANNEL);
+  /* Get target Client ID */
+  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);
+    goto out;
+  }
+  client_id = silc_id_payload_parse_id(tmp_id, tmp_len);
+  if (!client_id) {
+    silc_server_command_send_status_reply(cmd, SILC_COMMAND_CUMODE,
+                                         SILC_STATUS_ERR_NO_CLIENT_ID);
     goto out;
   }
 
-  /* 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_CMODE,
-                                         SILC_STATUS_ERR_NOT_ON_CHANNEL);
+  /* Get target client's entry */
+  target_client = silc_idlist_find_client_by_id(server->local_list, 
+                                               client_id, NULL);
+  if (!target_client) {
+    target_client = silc_idlist_find_client_by_id(server->global_list, 
+                                                 client_id, NULL);
+  }
+
+  /* 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_CUMODE,
+                                         SILC_STATUS_ERR_USER_NOT_ON_CHANNEL);
     goto out;
   }
 
   /* Get entry to the channel user list */
   silc_list_start(channel->user_list);
   while ((chl = silc_list_get(channel->user_list)) != SILC_LIST_END)
-    if (chl->client == client)
+    if (chl->client == target_client)
       break;
 
-  /* 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);
+  /* 
+   * Change the mode 
+   */
+
+  /* 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) {
+    silc_server_command_send_status_reply(cmd, SILC_COMMAND_CUMODE,
+                                         SILC_STATUS_ERR_NOT_YOU);
     goto out;
   }
 
-  /*
-   * Check the modes. Modes that requires nothing special operation are
-   * not checked here.
-   */
-
-  if (mode_mask & SILC_CHANNEL_MODE_PRIVKEY) {
-    /* Channel uses private keys to protect traffic. Client(s) has set the
-       key locally they want to use, server does not know that key. */
-    /* Nothing interesting to do here now */
+  if (target_mask & SILC_CHANNEL_UMODE_CHANFO) {
+    /* Cannot promote anyone to channel founder */
+    silc_server_command_send_status_reply(cmd, SILC_COMMAND_CUMODE,
+                                         SILC_STATUS_ERR_NOT_YOU);
+    goto out;
   } else {
-    if (channel->mode & SILC_CHANNEL_MODE_PRIVKEY) {
-      /* The mode is removed and we need to generate and distribute
-        new channel key. Clients are not using private channel keys
-        anymore after this. */
-
-      /* XXX Duplicated code, make own function for this!! LEAVE uses this
-        as well */
-
-      /* Re-generate channel key */
-      silc_server_create_channel_key(server, channel, 0);
-      
-      /* Encode channel key payload to be distributed on the channel */
-      packet = 
-       silc_channel_key_payload_encode(tmp_len2, tmp_id,
-                                       strlen(channel->channel_key->
-                                              cipher->name),
-                                       channel->channel_key->cipher->name,
-                                       channel->key_len / 8, channel->key);
-      
-      /* If we are normal server then we will send it to our router.  If we
-        are router we will send it to all local servers that has clients on
-        the channel */
-      if (server->server_type == SILC_SERVER) {
-       if (!server->standalone)
-         silc_server_packet_send(server, 
-                                 cmd->server->router->connection,
-                                 SILC_PACKET_CHANNEL_KEY, 0, packet->data,
-                                 packet->len, TRUE);
+    if (chl->mode & SILC_CHANNEL_UMODE_CHANFO) {
+      if (target_client == client) {
+       /* Remove channel founder rights from itself */
+       chl->mode &= ~SILC_CHANNEL_UMODE_CHANFO;
+       notify = TRUE;
       } else {
-       
+       silc_server_command_send_status_reply(cmd, SILC_COMMAND_CUMODE,
+                                             SILC_STATUS_ERR_NOT_YOU);
+       goto out;
       }
-      
-      /* Send to locally connected clients on the channel */
-      silc_server_packet_send_local_channel(server, channel, 
-                                           SILC_PACKET_CHANNEL_KEY, 0,
-                                           packet->data, packet->len, FALSE);
-      silc_buffer_free(packet);
     }
   }
-  
-  if (mode_mask & SILC_CHANNEL_MODE_ULIMIT) {
-    /* User limit is set on channel */
-    unsigned int 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);
-       goto out;
-      }
-    } else {
-      SILC_GET32_MSB(user_limit, tmp);
-      channel->mode_data.user_limit = user_limit;
-    }
-  } else {
-    if (channel->mode & SILC_CHANNEL_MODE_ULIMIT)
-      /* User limit mode is unset. Remove user limit */
-      channel->mode_data.user_limit = 0;
+
+  if (target_mask & SILC_CHANNEL_UMODE_CHANOP) {
+    /* Promote to operator */
+    if (!(chl->mode & SILC_CHANNEL_UMODE_CHANOP)) {
+      chl->mode |= SILC_CHANNEL_UMODE_CHANOP;
+      notify = TRUE;
+    }
+  } else {
+    if (chl->mode & SILC_CHANNEL_UMODE_CHANOP) {
+      /* Demote to normal user */
+      chl->mode &= ~SILC_CHANNEL_UMODE_CHANOP;
+      notify = TRUE;
+    }
+  }
+
+  idp = silc_id_payload_encode(client->id, SILC_ID_CLIENT);
+
+  /* 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,
+                                      idp->data, idp->len,
+                                      tmp_mask, 4, 
+                                      tmp_id, tmp_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,
+                                    target_mask, client->id, 
+                                    SILC_ID_CLIENT_LEN,
+                                    target_client->id, 
+                                    SILC_ID_CLIENT_LEN);
+  }
+
+  /* Send command reply to sender */
+  packet = silc_command_reply_payload_encode_va(SILC_COMMAND_CUMODE,
+                                               SILC_STATUS_OK, ident, 2,
+                                               2, tmp_mask, 4,
+                                               3, tmp_id, tmp_len);
+  silc_server_packet_send(server, cmd->sock, SILC_PACKET_COMMAND_REPLY, 0, 
+                         packet->data, packet->len, FALSE);
+    
+  silc_buffer_free(packet);
+  silc_free(channel_id);
+  silc_free(client_id);
+  silc_buffer_free(idp);
+
+ out:
+  silc_server_command_free(cmd);
+}
+
+/* Server side of KICK command. Kicks client out of channel. */
+
+SILC_SERVER_CMD_FUNC(kick)
+{
+  SilcServerCommandContext cmd = (SilcServerCommandContext)context;
+  SilcServer server = cmd->server;
+  SilcClientEntry client = (SilcClientEntry)cmd->sock->user_data;
+  SilcClientEntry target_client;
+  SilcChannelID *channel_id;
+  SilcClientID *client_id;
+  SilcChannelEntry channel;
+  SilcChannelClientEntry chl;
+  SilcBuffer idp;
+  unsigned int tmp_len;
+  unsigned char *tmp, *comment;
+
+  SILC_SERVER_COMMAND_CHECK_ARGC(SILC_COMMAND_LEAVE, cmd, 1, 3);
+
+  /* Get Channel ID */
+  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);
+    goto out;
+  }
+  channel_id = silc_id_payload_parse_id(tmp, tmp_len);
+  if (!channel_id) {
+    silc_server_command_send_status_reply(cmd, SILC_COMMAND_KICK,
+                                         SILC_STATUS_ERR_NO_CHANNEL_ID);
+    goto out;
   }
 
-  if (mode_mask & SILC_CHANNEL_MODE_PASSPHRASE) {
-    if (!(channel->mode & SILC_CHANNEL_MODE_PASSPHRASE)) {
-      /* Passphrase has been set to channel */
-      
-      /* Get the passphrase */
-      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);
-       goto out;
-      }
-
-      /* Save the passphrase */
-      channel->mode_data.passphrase = strdup(tmp);
-    }
-  } else {
-    if (channel->mode & SILC_CHANNEL_MODE_PASSPHRASE) {
-      /* Passphrase mode is unset. remove the passphrase */
-      if (channel->mode_data.passphrase) {
-       silc_free(channel->mode_data.passphrase);
-       channel->mode_data.passphrase = NULL;
-      }
+  /* Get channel entry */
+  channel = silc_idlist_find_channel_by_id(server->local_list, 
+                                          channel_id, NULL);
+  if (!channel) {
+    channel = silc_idlist_find_channel_by_id(server->local_list, 
+                                            channel_id, NULL);
+    if (!channel) {
+      silc_server_command_send_status_reply(cmd, SILC_COMMAND_KICK,
+                                           SILC_STATUS_ERR_NO_SUCH_CHANNEL);
+      goto out;
     }
   }
 
-  if (mode_mask & SILC_CHANNEL_MODE_BAN) {
-    if (!(channel->mode & SILC_CHANNEL_MODE_BAN)) {
-      /* Ban list is specified for channel */
+  /* Check whether sender is on the channel */
+  if (!silc_server_client_on_channel(client, channel)) {
+    silc_server_command_send_status_reply(cmd, SILC_COMMAND_KICK,
+                                         SILC_STATUS_ERR_NOT_ON_CHANNEL);
+    goto out;
+  }
 
-      /* Get ban list */
-      tmp = silc_argument_get_arg_type(cmd->args, 5, NULL);
-      if (!tmp) {
-       silc_server_command_send_status_reply(cmd, SILC_COMMAND_CMODE,
-                                             SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
+  /* Check that the kicker is channel operator or channel founder */
+  silc_list_start(channel->user_list);
+  while ((chl = silc_list_get(channel->user_list)) != SILC_LIST_END) {
+    if (chl->client == client) {
+      if (chl->mode == SILC_CHANNEL_UMODE_NONE) {
+       silc_server_command_send_status_reply(cmd, SILC_COMMAND_KICK,
+                                             SILC_STATUS_ERR_NO_CHANNEL_PRIV);
        goto out;
       }
-
-      /* XXX check that channel founder is not banned */
-
-      /* Save the ban list */
-      channel->mode_data.ban_list = strdup(tmp);
-    }
-  } else {
-    if (channel->mode & SILC_CHANNEL_MODE_BAN) {
-      /* Ban mode is unset. Remove the entire ban list */
-      if (channel->mode_data.ban_list) {
-       silc_free(channel->mode_data.ban_list);
-       channel->mode_data.ban_list = NULL;
-      }
+      break;
     }
   }
+  
+  /* Get target Client ID */
+  tmp = silc_argument_get_arg_type(cmd->args, 2, &tmp_len);
+  if (!tmp) {
+    silc_server_command_send_status_reply(cmd, SILC_COMMAND_KICK,
+                                         SILC_STATUS_ERR_NO_CLIENT_ID);
+    goto out;
+  }
+  client_id = silc_id_payload_parse_id(tmp, tmp_len);
+  if (!client_id) {
+    silc_server_command_send_status_reply(cmd, SILC_COMMAND_KICK,
+                                         SILC_STATUS_ERR_NO_CLIENT_ID);
+    goto out;
+  }
 
-  if (mode_mask & SILC_CHANNEL_MODE_INVITE_LIST) {
-    if (!(channel->mode & SILC_CHANNEL_MODE_INVITE_LIST)) {
-      /* Invite list is specified for channel */
+  /* Get target client's entry */
+  target_client = silc_idlist_find_client_by_id(server->local_list, 
+                                               client_id, NULL);
+  if (!target_client) {
+    target_client = silc_idlist_find_client_by_id(server->global_list, 
+                                                 client_id, NULL);
+  }
 
-      /* Get invite list */
-      tmp = silc_argument_get_arg_type(cmd->args, 6, NULL);
-      if (!tmp) {
-       silc_server_command_send_status_reply(cmd, SILC_COMMAND_CMODE,
-                                             SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
+  /* Check that the target client is not channel founder. Channel founder
+     cannot be kicked from the channel. */
+  silc_list_start(channel->user_list);
+  while ((chl = silc_list_get(channel->user_list)) != SILC_LIST_END) {
+    if (chl->client == target_client) {
+      if (chl->mode & SILC_CHANNEL_UMODE_CHANFO) {
+       silc_server_command_send_status_reply(cmd, SILC_COMMAND_KICK,
+                                 SILC_STATUS_ERR_NO_CHANNEL_FOPRIV);
        goto out;
       }
-
-      /* Save the invite linst */
-      channel->mode_data.invite_list = strdup(tmp);
-    }
-  } else {
-    if (channel->mode & SILC_CHANNEL_MODE_INVITE_LIST) {
-      /* Invite list mode is unset. Remove the entire invite list */
-      if (channel->mode_data.invite_list) {
-       silc_free(channel->mode_data.invite_list);
-       channel->mode_data.invite_list = NULL;
-      }
+      break;
     }
   }
+  
+  /* 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;
+  }
 
-  if (mode_mask & SILC_CHANNEL_MODE_CIPHER) {
-    if (!(channel->mode & SILC_CHANNEL_MODE_CIPHER)) {
-      /* Cipher to use protect the traffic */
-      unsigned int key_len = 128;
-      char *cp;
+  /* Get comment */
+  tmp_len = 0;
+  comment = silc_argument_get_arg_type(cmd->args, 3, &tmp_len);
+  if (tmp_len > 128)
+    comment = NULL;
 
-      /* Get cipher */
-      tmp = silc_argument_get_arg_type(cmd->args, 8, NULL);
-      if (!tmp) {
-       silc_server_command_send_status_reply(cmd, SILC_COMMAND_CMODE,
-                                             SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
-       goto out;
-      }
+  /* Send command reply to sender */
+  silc_server_command_send_status_reply(cmd, SILC_COMMAND_KICK, 
+                                       SILC_STATUS_OK);
 
-      cp = strchr(tmp, ':');
-      if (cp) {
-       key_len = atoi(cp);
-       *cp = '\0';
-      }
+  /* Send KICKED notify to local clients on the channel */
+  idp = silc_id_payload_encode(target_client->id, SILC_ID_CLIENT);
+  silc_server_send_notify_to_channel(server, NULL, channel, FALSE,
+                                    SILC_NOTIFY_TYPE_KICKED, 
+                                    comment ? 2 : 1,
+                                    idp->data, idp->len,
+                                    comment, comment ? strlen(comment) : 0);
+  silc_buffer_free(idp);
 
-      /* XXX Duplicated code, make own function for this!! */
-    
-      /* Delete old cipher and allocate the new one */
-      silc_cipher_free(channel->channel_key);
-      silc_cipher_alloc(tmp, &channel->channel_key);
+  /* Remove the client from the channel. If the channel does not exist
+     after removing the client then the client kicked itself off the channel
+     and we don't have to send anything after that. */
+  if (!silc_server_remove_from_one_channel(server, NULL, channel, 
+                                          target_client, FALSE))
+    goto out;
 
-      key_len /= 8;
-      if (key_len > 32)
-       key_len = 32;
+  /* Send KICKED notify to primary route */
+  if (!server->standalone)
+    silc_server_send_notify_kicked(server, server->router->connection,
+                                  server->server_type == SILC_ROUTER ?
+                                  TRUE : FALSE, channel,
+                                  target_client->id, SILC_ID_CLIENT_LEN,
+                                  comment);
 
-      /* Re-generate channel key */
-      silc_server_create_channel_key(server, channel, key_len);
-    
-      /* Encode channel key payload to be distributed on the channel */
-      packet = 
-       silc_channel_key_payload_encode(tmp_len2, tmp_id,
-                                       strlen(channel->channel_key->
-                                              cipher->name),
-                                       channel->channel_key->cipher->name,
-                                       channel->key_len / 8, channel->key);
-    
-      /* If we are normal server then we will send it to our router.  If we
-        are router we will send it to all local servers that has clients on
-        the channel */
-      if (server->server_type == SILC_SERVER) {
-       if (!server->standalone)
-         silc_server_packet_send(server, 
-                                 cmd->server->router->connection,
-                                 SILC_PACKET_CHANNEL_KEY, 0, packet->data,
-                                 packet->len, TRUE);
-      } else {
-       
-      }
-    
-      /* Send to locally connected clients on the channel */
-      silc_server_packet_send_local_channel(server, channel, 
-                                           SILC_PACKET_CHANNEL_KEY, 0,
-                                         packet->data, packet->len, FALSE);
-      silc_buffer_free(packet);
-    }
-  } else {
-    if (channel->mode & SILC_CHANNEL_MODE_CIPHER) {
-      /* Cipher mode is unset. Remove the cipher and revert back to 
-        default cipher */
+  /* Re-generate channel key */
+  silc_server_create_channel_key(server, channel, 0);
 
-      if (channel->mode_data.cipher) {
-       silc_free(channel->mode_data.cipher);
-       channel->mode_data.cipher = NULL;
-       channel->mode_data.key_len = 0;
-      }
+  /* Send the channel key to the channel. The key of course is not sent
+     to the client who was kicked off the channel. */
+  silc_server_send_channel_key(server, target_client->connection, channel, 
+                              server->server_type == SILC_ROUTER ? 
+                              FALSE : !server->standalone);
 
-      /* Generate new cipher and key for the channel */
+ out:
+  silc_server_command_free(cmd);
+}
 
-      /* XXX Duplicated code, make own function for this!! */
+/* Server side of OPER command. Client uses this comand to obtain server
+   operator privileges to this server/router. */
 
-      /* Delete old cipher and allocate default one */
-      silc_cipher_free(channel->channel_key);
-      if (!channel->cipher)
-       silc_cipher_alloc("twofish", &channel->channel_key);
-      else
-       silc_cipher_alloc(channel->cipher, &channel->channel_key);
+SILC_SERVER_CMD_FUNC(oper)
+{
+  SilcServerCommandContext cmd = (SilcServerCommandContext)context;
+  SilcServer server = cmd->server;
+  SilcClientEntry client = (SilcClientEntry)cmd->sock->user_data;
+  unsigned char *username, *auth;
+  unsigned int tmp_len;
+  SilcServerConfigSectionAdminConnection *admin;
+  SilcIDListData idata = (SilcIDListData)client;
 
-      /* Re-generate channel key */
-      silc_server_create_channel_key(server, channel, 0);
-      
-      /* Encode channel key payload to be distributed on the channel */
-      packet = 
-       silc_channel_key_payload_encode(tmp_len2, tmp_id,
-                                       strlen(channel->channel_key->
-                                              cipher->name),
-                                       channel->channel_key->cipher->name,
-                                       channel->key_len / 8, channel->key);
-      
-      /* If we are normal server then we will send it to our router.  If we
-        are router we will send it to all local servers that has clients on
-        the channel */
-      if (server->server_type == SILC_SERVER) {
-       if (!server->standalone)
-         silc_server_packet_send(server, 
-                                 cmd->server->router->connection,
-                                 SILC_PACKET_CHANNEL_KEY, 0, packet->data,
-                                 packet->len, TRUE);
-      } else {
-       
-      }
-      
-      /* Send to locally connected clients on the channel */
-      silc_server_packet_send_local_channel(server, channel, 
-                                           SILC_PACKET_CHANNEL_KEY, 0,
-                                           packet->data, packet->len, FALSE);
-      silc_buffer_free(packet);
+  SILC_SERVER_COMMAND_CHECK_ARGC(SILC_COMMAND_OPER, cmd, 1, 2);
+
+  if (!client || cmd->sock->type != SILC_SOCKET_TYPE_CLIENT)
+    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_OPER,
+                                         SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
+    goto out;
+  }
+
+  /* Get the admin configuration */
+  admin = silc_server_config_find_admin(server->config, cmd->sock->ip,
+                                       username, client->nickname);
+  if (!admin) {
+    admin = silc_server_config_find_admin(server->config, cmd->sock->hostname,
+                                         username, client->nickname);
+    if (!admin) {
+      silc_server_command_send_status_reply(cmd, SILC_COMMAND_OPER,
+                                           SILC_STATUS_ERR_AUTH_FAILED);
+      goto out;
     }
   }
 
-  /* Finally, set the mode */
-  channel->mode = mode_mask;
+  /* 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_OPER,
+                                         SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
+    goto out;
+  }
 
-  /* Send CMODE_CHANGE notify */
-  cidp = silc_id_payload_encode(client->id, SILC_ID_CLIENT);
-  silc_server_send_notify_to_channel(server, channel, TRUE,
-                                    SILC_NOTIFY_TYPE_CMODE_CHANGE, 2,
-                                    cidp->data, cidp->len, 
-                                    tmp_mask, tmp_len);
-  silc_free(cidp);
+  /* 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_OPER,
+                                         SILC_STATUS_ERR_AUTH_FAILED);
+    goto out;
+  }
 
-  /* Send command reply to sender */
-  packet = silc_command_reply_payload_encode_va(SILC_COMMAND_CMODE,
-                                               SILC_STATUS_OK, 0, 1,
-                                               2, 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);
+  /* Client is now server operator */
+  client->mode |= SILC_UMODE_SERVER_OPERATOR;
+
+  /* Send UMODE change to primary router */
+  if (!server->standalone)
+    silc_server_send_notify_umode(server, server->router->connection, TRUE,
+                                 client->id, SILC_ID_CLIENT_LEN,
+                                 client->mode);
+
+  /* Send reply to the sender */
+  silc_server_command_send_status_reply(cmd, SILC_COMMAND_OPER,
+                                       SILC_STATUS_OK);
 
  out:
   silc_server_command_free(cmd);
 }
 
-/* Server side of CUMODE command. Changes client's mode on a channel. */
+/* Server side of SILCOPER command. Client uses this comand to obtain router
+   operator privileges to this router. */
 
-SILC_SERVER_CMD_FUNC(cumode)
+SILC_SERVER_CMD_FUNC(silcoper)
 {
   SilcServerCommandContext cmd = (SilcServerCommandContext)context;
   SilcServer server = cmd->server;
   SilcClientEntry client = (SilcClientEntry)cmd->sock->user_data;
-  SilcChannelID *channel_id;
-  SilcClientID *client_id;
-  SilcChannelEntry channel;
-  SilcClientEntry target_client;
-  SilcChannelClientEntry chl;
-  SilcBuffer packet, idp;
-  unsigned char *tmp_id, *tmp_mask;
-  unsigned int target_mask, sender_mask, tmp_len;
-  int notify = FALSE;
-
-  SILC_SERVER_COMMAND_CHECK_ARGC(SILC_COMMAND_CUMODE, cmd, 3, 3);
+  unsigned char *username, *auth;
+  unsigned int tmp_len;
+  SilcServerConfigSectionAdminConnection *admin;
+  SilcIDListData idata = (SilcIDListData)client;
 
-  /* Get Channel ID */
-  tmp_id = silc_argument_get_arg_type(cmd->args, 1, &tmp_len);
-  if (!tmp_id) {
-    silc_server_command_send_status_reply(cmd, SILC_COMMAND_CUMODE,
-                                         SILC_STATUS_ERR_NO_CHANNEL_ID);
-    goto out;
-  }
-  channel_id = silc_id_payload_parse_id(tmp_id, tmp_len);
+  SILC_SERVER_COMMAND_CHECK_ARGC(SILC_COMMAND_SILCOPER, cmd, 1, 2);
 
-  /* Get channel entry */
-  channel = silc_idlist_find_channel_by_id(server->local_list, 
-                                          channel_id, NULL);
-  if (!channel) {
-    silc_server_command_send_status_reply(cmd, SILC_COMMAND_CUMODE,
-                                         SILC_STATUS_ERR_NO_SUCH_CHANNEL);
+  if (!client || cmd->sock->type != SILC_SOCKET_TYPE_CLIENT)
     goto out;
-  }
 
-  /* Check whether sender is on the channel */
-  if (!silc_server_client_on_channel(client, channel)) {
-    silc_server_command_send_status_reply(cmd, SILC_COMMAND_CUMODE,
-                                         SILC_STATUS_ERR_NOT_ON_CHANNEL);
+  /* 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);
     goto out;
   }
 
-  /* Check that client has rights to change other's rights */
-  silc_list_start(channel->user_list);
-  while ((chl = silc_list_get(channel->user_list)) != SILC_LIST_END) {
-    if (chl->client == client) {
-      if (!(chl->mode & SILC_CHANNEL_UMODE_CHANFO) &&
-         !(chl->mode & SILC_CHANNEL_UMODE_CHANOP)) {
-       silc_server_command_send_status_reply(cmd, SILC_COMMAND_CUMODE,
-                                             SILC_STATUS_ERR_NO_CHANNEL_PRIV);
-       goto out;
-      }
-
-      sender_mask = chl->mode;
-      break;
+  /* 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;
     }
   }
-  
-  /* 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,
+
+  /* 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);
     goto out;
   }
-  SILC_GET32_MSB(target_mask, tmp_mask);
 
-  /* Get target Client ID */
-  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_CHANNEL_ID);
+  /* 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);
     goto out;
   }
-  client_id = silc_id_payload_parse_id(tmp_id, tmp_len);
 
-  /* Get target client's entry */
-  target_client = silc_idlist_find_client_by_id(server->local_list, 
-                                               client_id, NULL);
-  if (!target_client) {
-    /* XXX If target client is not one of mine send to primary route */
-  }
+  /* Client is now router operator */
+  client->mode |= SILC_UMODE_ROUTER_OPERATOR;
 
-  /* 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_CUMODE,
-                                         SILC_STATUS_ERR_USER_NOT_ON_CHANNEL);
-    goto out;
-  }
+  /* Send UMODE change to primary router */
+  if (!server->standalone)
+    silc_server_send_notify_umode(server, server->router->connection, TRUE,
+                                 client->id, SILC_ID_CLIENT_LEN,
+                                 client->mode);
 
-  /* Get entry to the channel user list */
-  silc_list_start(channel->user_list);
-  while ((chl = silc_list_get(channel->user_list)) != SILC_LIST_END)
-    if (chl->client == target_client)
-      break;
+  /* Send reply to the sender */
+  silc_server_command_send_status_reply(cmd, SILC_COMMAND_SILCOPER,
+                                       SILC_STATUS_OK);
 
-  /* 
-   * Change the mode 
-   */
+ out:
+  silc_server_command_free(cmd);
+}
 
-  /* 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) {
-    silc_server_command_send_status_reply(cmd, SILC_COMMAND_CUMODE,
-                                         SILC_STATUS_ERR_NOT_YOU);
+/* 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;
+  unsigned int tmp_len;
+  unsigned int port = SILC_PORT;
+
+  SILC_SERVER_COMMAND_CHECK_ARGC(SILC_COMMAND_CONNECT, cmd, 1, 2);
+
+  if (!client || cmd->sock->type != SILC_SOCKET_TYPE_CLIENT)
     goto out;
-  }
 
-  if (target_mask & SILC_CHANNEL_UMODE_CHANFO) {
-    /* Cannot promote anyone to channel founder */
-    silc_server_command_send_status_reply(cmd, SILC_COMMAND_CUMODE,
-                                         SILC_STATUS_ERR_NOT_YOU);
+  /* 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;
-  } else {
-    if (chl->mode & SILC_CHANNEL_UMODE_CHANFO) {
-      if (target_client == client) {
-       /* Remove channel founder rights from itself */
-       chl->mode &= ~SILC_CHANNEL_UMODE_CHANFO;
-       notify = TRUE;
-      } else {
-       silc_server_command_send_status_reply(cmd, SILC_COMMAND_CUMODE,
-                                             SILC_STATUS_ERR_NOT_YOU);
-       goto out;
-      }
-    }
   }
 
-  if (target_mask & SILC_CHANNEL_UMODE_CHANOP) {
-    /* Promote to operator */
-    if (!(chl->mode & SILC_CHANNEL_UMODE_CHANOP)) {
-      chl->mode |= SILC_CHANNEL_UMODE_CHANOP;
-      notify = TRUE;
-    }
-  } else {
-    if (chl->mode & SILC_CHANNEL_UMODE_CHANOP) {
-      /* Demote to normal user */
-      chl->mode &= ~SILC_CHANNEL_UMODE_CHANOP;
-      notify = TRUE;
-    }
+  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;
   }
 
-  /* Send notify to channel, notify only if mode was actually changed. */
-  if (notify) {
-    idp = silc_id_payload_encode(client->id, SILC_ID_CLIENT);
-    silc_server_send_notify_to_channel(server, channel, TRUE,
-                                      SILC_NOTIFY_TYPE_CUMODE_CHANGE, 3,
-                                      idp->data, idp->len,
-                                      tmp_mask, 4, tmp_id, tmp_len);
-    silc_buffer_free(idp);
+  /* 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;
   }
 
-  /* Send command reply to sender */
-  packet = silc_command_reply_payload_encode_va(SILC_COMMAND_CUMODE,
-                                               SILC_STATUS_OK, 0, 2,
-                                               2, tmp_mask, 4,
-                                               3, tmp_id, tmp_len);
-  silc_server_packet_send(server, cmd->sock, SILC_PACKET_COMMAND_REPLY, 0, 
-                         packet->data, packet->len, FALSE);
-    
-  silc_buffer_free(packet);
-  silc_free(channel_id);
-  silc_free(client_id);
+  /* Get port */
+  tmp = silc_argument_get_arg_type(cmd->args, 2, &tmp_len);
+  if (tmp)
+    SILC_GET32_MSB(port, tmp);
 
- out:
-  silc_server_command_free(cmd);
-}
+  /* Create the connection. It is done with timeout and is async. */
+  silc_server_create_connection(server, host, port);
 
-/* Server side of KICK command. Kicks client out of channel. */
+  /* Send reply to the sender */
+  silc_server_command_send_status_reply(cmd, SILC_COMMAND_CONNECT,
+                                       SILC_STATUS_OK);
 
-SILC_SERVER_CMD_FUNC(kick)
-{
+ out:
+  silc_server_command_free(cmd);
 }
 
 SILC_SERVER_CMD_FUNC(restart)
 {
 }
+
+/* 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;
+  unsigned int tmp_len;
+  unsigned char *name;
+  unsigned int port = SILC_PORT;
+
+  SILC_SERVER_COMMAND_CHECK_ARGC(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, 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;
+  silc_server_free_sock_user_data(server, sock);
+  silc_server_close_connection(server, sock);
+  
+ out:
+  silc_server_command_free(cmd);
 }
+
+/* Server side command of SHUTDOWN. Shutdowns the server and closes all
+   active connections. */
  
-SILC_SERVER_CMD_FUNC(die)
+SILC_SERVER_CMD_FUNC(shutdown)
 {
+  SilcServerCommandContext cmd = (SilcServerCommandContext)context;
+  SilcServer server = cmd->server;
+  SilcClientEntry client = (SilcClientEntry)cmd->sock->user_data;
+
+  SILC_SERVER_COMMAND_CHECK_ARGC(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);
 }
  
-SILC_SERVER_CMD_FUNC(silcoper)
-{
-}
-
 /* Server side command of LEAVE. Removes client from a channel. */
 
 SILC_SERVER_CMD_FUNC(leave)
@@ -2743,13 +3840,21 @@ SILC_SERVER_CMD_FUNC(leave)
     goto out;
   }
   id = silc_id_payload_parse_id(tmp, len);
+  if (!id) {
+    silc_server_command_send_status_reply(cmd, SILC_COMMAND_LEAVE,
+                                         SILC_STATUS_ERR_NO_CHANNEL_ID);
+    goto out;
+  }
 
   /* Get channel entry */
   channel = silc_idlist_find_channel_by_id(server->local_list, id, NULL);
   if (!channel) {
-    silc_server_command_send_status_reply(cmd, SILC_COMMAND_LEAVE,
-                                         SILC_STATUS_ERR_NO_SUCH_CHANNEL);
-    goto out;
+    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);
+      goto out;
+    }
   }
 
   /* Check whether this client is on the channel */
@@ -2760,12 +3865,12 @@ SILC_SERVER_CMD_FUNC(leave)
   }
 
   /* Notify routers that they should remove this client from their list
-     of clients on the channel. */
+     of clients on the channel. Send LEAVE notify type. */
   if (!server->standalone)
-    silc_server_send_remove_channel_user(server, 
-                                        server->router->connection,
-                                        server->server_type == SILC_ROUTER ?
-                                        TRUE : FALSE, id_entry->id, id);
+    silc_server_send_notify_leave(server, server->router->connection,
+                                 server->server_type == SILC_ROUTER ?
+                                 TRUE : FALSE, channel, id_entry->id,
+                                 SILC_ID_CLIENT_LEN);
 
   /* Remove client from channel */
   i = silc_server_remove_from_one_channel(server, sock, channel, id_entry,
@@ -2795,7 +3900,7 @@ SILC_SERVER_CMD_FUNC(leave)
       silc_server_packet_send(server, 
                              cmd->server->router->connection,
                              SILC_PACKET_CHANNEL_KEY, 0, packet->data,
-                             packet->len, TRUE);
+                             packet->len, FALSE);
   } else {
 
   }
@@ -2812,114 +3917,94 @@ SILC_SERVER_CMD_FUNC(leave)
   silc_server_command_free(cmd);
 }
 
-/* Server side of command NAMES. Resolves clients and their names currently
-   joined on the requested channel. The name list is sent back to the
-   client. */
+/* Server side of command USERS. Resolves clients and their USERS currently
+   joined on the requested channel. The list of Client ID's and their modes
+   on the channel is sent back. */
 
-SILC_SERVER_CMD_FUNC(names)
+SILC_SERVER_CMD_FUNC(users)
 {
   SilcServerCommandContext cmd = (SilcServerCommandContext)context;
   SilcServer server = cmd->server;
   SilcChannelEntry channel;
-  SilcChannelClientEntry chl;
   SilcChannelID *id;
   SilcBuffer packet;
-  unsigned int i, len, len2, tmp_len;
-  unsigned char *tmp;
-  char *name_list = NULL, *n;
+  unsigned char *channel_id;
+  unsigned int channel_id_len;
   SilcBuffer client_id_list;
   SilcBuffer client_mode_list;
+  unsigned char lc[4];
+  unsigned int list_count = 0;
+  unsigned short ident = silc_command_get_ident(cmd->payload);
 
-  SILC_SERVER_COMMAND_CHECK_ARGC(SILC_COMMAND_NAMES, cmd, 1, 2);
+  SILC_SERVER_COMMAND_CHECK_ARGC(SILC_COMMAND_USERS, cmd, 1, 1);
 
   /* Get Channel ID */
-  tmp = silc_argument_get_arg_type(cmd->args, 1, &tmp_len);
-  if (!tmp) {
-    silc_server_command_send_status_reply(cmd, SILC_COMMAND_LEAVE,
+  channel_id = silc_argument_get_arg_type(cmd->args, 1, &channel_id_len);
+  if (!channel_id) {
+    silc_server_command_send_status_reply(cmd, SILC_COMMAND_USERS,
+                                         SILC_STATUS_ERR_NO_CHANNEL_ID);
+    goto out;
+  }
+  id = silc_id_payload_parse_id(channel_id, channel_id_len);
+  if (!id) {
+    silc_server_command_send_status_reply(cmd, SILC_COMMAND_USERS,
                                          SILC_STATUS_ERR_NO_CHANNEL_ID);
     goto out;
   }
-  id = silc_id_payload_parse_id(tmp, tmp_len);
 
-  /* Check whether the channel exists. If we are normal server and the
-     channel does not exist we will send this same command to our router
-     which will know if the channel exists. */
+  /* If we are server and we don't know about this channel we will send
+     the command to our router. If we know about the channel then we also
+     have the list of users already. */
   channel = silc_idlist_find_channel_by_id(server->local_list, id, NULL);
   if (!channel) {
-    if (server->server_type == SILC_SERVER && !server->standalone) {
-      /* XXX Send names command */
-
+    if (server->server_type == SILC_SERVER && !server->standalone &&
+       !cmd->pending) {
+      SilcBuffer tmpbuf;
+      
+      silc_command_set_ident(cmd->payload, silc_rng_get_rn16(server->rng));
+      tmpbuf = silc_command_payload_encode_payload(cmd->payload);
+      
+      /* Send USERS command */
+      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 */
+      silc_server_command_pending(server, SILC_COMMAND_USERS, 
+                                 silc_command_get_ident(cmd->payload),
+                                 silc_server_command_destructor,
+                                 silc_server_command_users,
+                                 silc_server_command_dup(cmd));
       cmd->pending = TRUE;
-      silc_server_command_pending(server, SILC_COMMAND_NAMES, 0,
-                                 silc_server_command_names, context);
+      silc_command_set_ident(cmd->payload, ident);
+      
+      silc_buffer_free(tmpbuf);
+      silc_free(id);
       return;
     }
 
-    silc_server_command_send_status_reply(cmd, SILC_COMMAND_INVITE,
-                                         SILC_STATUS_ERR_NO_SUCH_CHANNEL);
-    goto out;
-  }
-
-  /* Assemble the name list now */
-  name_list = NULL;
-  len = 0;
-  silc_list_start(channel->user_list);
-  i = 0;
-  while ((chl = silc_list_get(channel->user_list)) != SILC_LIST_END) {
-    n = chl->client->nickname;
-    if (n) {
-      len2 = strlen(n);
-      len += len2;
-      name_list = silc_realloc(name_list, sizeof(*name_list) * (len + 1));
-      memcpy(name_list + (len - len2), n, len2);
-      name_list[len] = 0;
-
-      if (i == silc_list_count(channel->user_list) - 1)
-       break;
-      memcpy(name_list + len, ",", 1);
-      len++;
-      i++;
+    /* We are router and we will check the global list as well. */
+    channel = silc_idlist_find_channel_by_id(server->global_list, id, NULL);
+    if (!channel) {
+      /* Channel really does not exist */
+      silc_server_command_send_status_reply(cmd, SILC_COMMAND_USERS,
+                                           SILC_STATUS_ERR_NO_SUCH_CHANNEL);
+      goto out;
     }
   }
-  if (!name_list)
-    name_list = "";
 
-  /* Assemble the Client ID list now */
-  client_id_list = silc_buffer_alloc((SILC_ID_CLIENT_LEN + 4) * 
-                                    silc_list_count(channel->user_list));
-  silc_buffer_pull_tail(client_id_list, SILC_BUFFER_END(client_id_list));
-  silc_list_start(channel->user_list);
-  while ((chl = silc_list_get(channel->user_list)) != SILC_LIST_END) {
-    SilcBuffer idp;
-
-    idp = silc_id_payload_encode(chl->client->id, SILC_ID_CLIENT);
-    silc_buffer_format(client_id_list,
-                      SILC_STR_UI_XNSTRING(idp->data, idp->len),
-                      SILC_STR_END);
-    silc_buffer_pull(client_id_list, idp->len);
-    silc_buffer_free(idp);
-  }
-  silc_buffer_push(client_id_list, 
-                  client_id_list->data - client_id_list->head);
+  /* Get the users list */
+  silc_server_get_users_on_channel(server, channel, &client_id_list,
+                                  &client_mode_list, &list_count);
 
-  /* Assemble mode list */
-  client_mode_list = silc_buffer_alloc(4 * 
-                                      silc_list_count(channel->user_list));
-  silc_buffer_pull_tail(client_mode_list, SILC_BUFFER_END(client_mode_list));
-  silc_list_start(channel->user_list);
-  while ((chl = silc_list_get(channel->user_list)) != SILC_LIST_END) {
-    SILC_PUT32_MSB(chl->mode, client_mode_list->data);
-    silc_buffer_pull(client_mode_list, 4);
-  }
-  silc_buffer_push(client_mode_list, 
-                  client_mode_list->data - client_mode_list->head);
+  /* List count */
+  SILC_PUT32_MSB(list_count, lc);
 
   /* Send reply */
-  packet = silc_command_reply_payload_encode_va(SILC_COMMAND_NAMES,
-                                               SILC_STATUS_OK, 0, 4,
-                                               2, tmp, tmp_len,
-                                               3, name_list, 
-                                               strlen(name_list),
+  packet = silc_command_reply_payload_encode_va(SILC_COMMAND_USERS,
+                                               SILC_STATUS_OK, ident, 4,
+                                               2, channel_id, channel_id_len,
+                                               3, lc, 4,
                                                4, client_id_list->data,
                                                client_id_list->len,
                                                5, client_mode_list->data,
@@ -2928,7 +4013,6 @@ SILC_SERVER_CMD_FUNC(names)
                          packet->data, packet->len, FALSE);
     
   silc_buffer_free(packet);
-  silc_free(name_list);
   silc_buffer_free(client_id_list);
   silc_buffer_free(client_mode_list);
   silc_free(id);