updates.
[silc.git] / apps / silcd / command.c
index 656cf67ce97084a0279780e0d9824b64df9f3a93..6792f3c0f72ad40082c4ea82814b007ceabcbec7 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,11 +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_users(SilcServer server,
-                                   SilcSocketConnection sock,
-                                   SilcChannelEntry channel,
-                                   int pending);
+SILC_TASK_CALLBACK(silc_server_command_process_timeout);
 
 /* Server command list. */
 SilcServerCommand silc_command_list[] =
@@ -49,31 +45,32 @@ 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(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(leave, LEAVE, SILC_CF_LAG_STRICT | SILC_CF_REG),
   SILC_SERVER_CMD(users, USERS, SILC_CF_LAG | SILC_CF_REG),
 
   { NULL, 0 },
@@ -117,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,
@@ -126,34 +151,11 @@ void silc_server_command_process(SilcServer server,
   SilcServerCommandContext ctx;
   SilcServerCommand *cmd;
 
-#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 */
@@ -162,31 +164,107 @@ 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. */
+  /* Get the command */
   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;
-      }
-    }
+    if (cmd->cmd == silc_command_get(ctx->payload))
+      break;
 
   if (cmd == NULL) {
     SILC_LOG_ERROR(("Unknown command, packet dropped"));
     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
@@ -198,6 +276,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)
 {
@@ -208,6 +287,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);
 }
 
@@ -243,6 +323,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;
     }
@@ -251,17 +332,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 */
@@ -351,17 +427,29 @@ silc_server_command_whois_parse(SilcServerCommandContext cmd,
 
     *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 > 3) {
-      for (k = 1, i = 4; i < argc; i++) {
-       tmp = silc_argument_get_arg_type(cmd->args, i, &len);
+    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);
+         (*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++;
        }
       }
     }
@@ -391,7 +479,7 @@ silc_server_command_whois_check(SilcServerCommandContext cmd,
   for (i = 0; i < clients_count; i++) {
     entry = clients[i];
 
-    if (!entry->nickname || !entry->username || !entry->userinfo) {
+    if (!entry->nickname || !entry->username) {
       SilcBuffer tmpbuf;
       unsigned short old_ident;
 
@@ -410,7 +498,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);
@@ -443,6 +533,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;
 
@@ -452,11 +551,15 @@ 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)
+      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];
@@ -464,7 +567,7 @@ silc_server_command_whois_send_reply(SilcServerCommandContext cmd,
 
       memset(uh, 0, sizeof(uh));
       memset(nh, 0, sizeof(nh));
-      
+
       strncat(nh, entry->nickname, strlen(entry->nickname));
       if (!strchr(entry->nickname, '@')) {
        strncat(nh, "@", 1);
@@ -484,25 +587,15 @@ silc_server_command_whois_send_reply(SilcServerCommandContext cmd,
       
       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);
+      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);
     }
     
     silc_server_packet_send(server, cmd->sock, SILC_PACKET_COMMAND_REPLY,
@@ -546,7 +639,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);
@@ -581,6 +676,10 @@ silc_server_command_whois_from_client(SilcServerCommandContext cmd)
     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 */
@@ -600,6 +699,10 @@ silc_server_command_whois_from_client(SilcServerCommandContext cmd)
       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);
     }
   }
   
@@ -624,8 +727,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;
   }
@@ -735,8 +837,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;
   }
@@ -766,93 +867,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
 
 ******************************************************************************/
 
-/* 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)
+static int
+silc_server_command_whowas_parse(SilcServerCommandContext cmd,
+                                char **nickname,
+                                char **server_name,
+                                int *count)
 {
-  SilcServer server = cmd->server;
-  int i;
-  SilcClientEntry entry;
-
-  for (i = 0; i < clients_count; i++) {
-    entry = clients[i];
+  unsigned char *tmp;
+  unsigned int len;
 
-    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_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);
+  tmp = silc_argument_get_arg_type(cmd->args, 1, &len);
+  if (!tmp) {
+    silc_server_command_send_status_reply(cmd, SILC_COMMAND_WHOWAS,
+                                         SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
+    return FALSE;
+  }
 
-      silc_buffer_free(tmpbuf);
-      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 */
+  tmp = silc_argument_get_arg_type(cmd->args, 2, NULL);
+  if (tmp)
+    *count = atoi(tmp);
+  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)
@@ -861,27 +944,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);
@@ -891,48 +987,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 = 0; 
-  SilcClientEntry *clients = NULL, entry;
-  SilcClientID **client_id = NULL;
-  unsigned int client_id_count = 0;
-  int i, ret = 0;
+  int count = 0, clients_count = 0;
+  SilcClientEntry *clients = NULL;
+  int ret = 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. */
@@ -945,16 +1039,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);
@@ -967,76 +1063,403 @@ 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. */
 
-  /* Parse the IDENTIFY request */
-  if (!silc_server_command_whois_parse(cmd, &client_id, &client_id_count,
-                                      &nick, &server_name, &count,
-                                      SILC_COMMAND_IDENTIFY))
+  /* Parse the whowas request */
+  if (!silc_server_command_whowas_parse(cmd, &nick, &server_name, &count))
     return 0;
 
-  /* Get all clients matching that ID or nickname from local list */
-  if (client_id_count) { 
-    /* Check all Client ID's received in the command packet */
-    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);
-  }
+  /* 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_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);
-    }
+    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_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 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 (nick)
+    silc_free(nick);
+  if (server_name)
+    silc_free(server_name);
+
+  return ret;
+}
+
+static int
+silc_server_command_whowas_from_server(SilcServerCommandContext cmd)
+{
+  SilcServer server = cmd->server;
+  char *nick = NULL, *server_name = NULL;
+  int count = 0, clients_count = 0;
+  SilcClientEntry *clients = NULL;
+  int ret = 0;
+
+  /* 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. */
+
+  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) {
+    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_WHOWAS,
+                                        SILC_STATUS_ERR_NO_SUCH_NICK,
+                                        3, nick, strlen(nick));
+    goto out;
+  }
+
+  /* Send the command reply to the client */
+  silc_server_command_whowas_send_reply(cmd, clients, clients_count);
+
+ out:
+  if (clients)
+    silc_free(clients);
+  if (nick)
+    silc_free(nick);
+  if (server_name)
+    silc_free(server_name);
+
+  return ret;
+}
+
+/* Server side of command WHOWAS. */
+
+SILC_SERVER_CMD_FUNC(whowas)
+{
+  SilcServerCommandContext cmd = (SilcServerCommandContext)context;
+  int ret = 0;
+
+  SILC_SERVER_COMMAND_CHECK_ARGC(SILC_COMMAND_WHOWAS, cmd, 1, 2);
+
+  if (cmd->sock->type == SILC_SOCKET_TYPE_CLIENT)
+    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);
+}
+
+/******************************************************************************
+
+                              IDENTIFY Functions
+
+******************************************************************************/
+
+/* 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;
+      
+      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));
+
+      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;
+    }
+  }
+
+  return TRUE;
+}
+
+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);
+
+  status = SILC_STATUS_OK;
+  if (clients_count > 1)
+    status = SILC_STATUS_LIST_START;
+
+  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_IDENTIFY,
+                                            SILC_STATUS_ERR_NO_SUCH_NICK,
+                                            3, entry->nickname, 
+                                            strlen(entry->nickname));
+      continue;
+    }
+
+    if (count && i - 1 == count)
+      break;
+
+    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 */
+    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);
+       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);
+    }
+  }
+}
+
+static int
+silc_server_command_identify_from_client(SilcServerCommandContext cmd)
+{
+  SilcServer server = cmd->server;
+  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;
+
+  /* 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;
+
+    old_ident = silc_command_get_ident(cmd->payload);
+    silc_command_set_ident(cmd->payload, silc_rng_get_rn16(server->rng));
+    tmpbuf = silc_command_payload_encode_payload(cmd->payload);
+
+    /* Send IDENTIFY command to our router */
+    silc_server_packet_send(server, (SilcSocketConnection)
+                           server->router->connection,
+                           SILC_PACKET_COMMAND, cmd->packet->flags,
+                           tmpbuf->data, tmpbuf->len, TRUE);
+
+    /* Reprocess this packet after received reply from router */
+    silc_server_command_pending(server, SILC_COMMAND_IDENTIFY, 
+                               silc_command_get_ident(cmd->payload),
+                               silc_server_command_destructor,
+                               silc_server_command_identify,
+                               silc_server_command_dup(cmd));
+    cmd->pending = TRUE;
+
+    silc_command_set_ident(cmd->payload, old_ident);
+
+    silc_buffer_free(tmpbuf);
+    ret = -1;
+    goto out;
+  }
+
+  /* We are ready to process the command request. Let's search for the
+     requested client and send reply to the requesting client. */
+
+  /* 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 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);
+    }
+  }
+  
+  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 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;
+  }
+
+  /* Send the command reply to the client */
+  silc_server_command_identify_send_reply(cmd, clients, clients_count);
 
  out:
   if (client_id_count) {
@@ -1137,8 +1560,7 @@ silc_server_command_identify_from_server(SilcServerCommandContext cmd)
 
   /* 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)) {
+  if (!silc_server_command_identify_check(cmd, clients, clients_count)) {
     ret = -1;
     goto out;
   }
@@ -1165,13 +1587,14 @@ silc_server_command_identify_from_server(SilcServerCommandContext cmd)
 SILC_SERVER_CMD_FUNC(identify)
 {
   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_IDENTIFY, cmd, 1, 3328);
 
   if (cmd->sock->type == SILC_SOCKET_TYPE_CLIENT)
     ret = silc_server_command_identify_from_client(cmd);
-  else
+  else if ((cmd->sock->type == SILC_SOCKET_TYPE_SERVER) |
+          (cmd->sock->type == SILC_SOCKET_TYPE_ROUTER))
     ret = silc_server_command_identify_from_server(cmd);
 
   if (!ret)
@@ -1207,6 +1630,9 @@ SILC_SERVER_CMD_FUNC(nick)
   SilcClientID *new_id;
   char *nick;
 
+  if (cmd->sock->type != SILC_SOCKET_TYPE_CLIENT)
+    goto out;
+
   SILC_SERVER_COMMAND_CHECK_ARGC(SILC_COMMAND_NICK, cmd, 1, 1);
 
   /* Check nickname */
@@ -1217,6 +1643,9 @@ SILC_SERVER_CMD_FUNC(nick)
     goto out;
   }
 
+  if (strlen(nick) > 128)
+    nick[127] = '\0';
+
   /* Create new Client ID */
   silc_id_create_client_id(cmd->server->id, cmd->server->rng, 
                           cmd->server->md5hash, nick,
@@ -1224,13 +1653,12 @@ SILC_SERVER_CMD_FUNC(nick)
 
   /* 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);
+     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);
 
   /* Remove old cache entry */
   silc_idcache_del_by_id(server->local_list->clients, SILC_ID_CLIENT, 
@@ -1253,11 +1681,11 @@ SILC_SERVER_CMD_FUNC(nick)
 
   /* Update client cache */
   silc_idcache_add(server->local_list->clients, client->nickname, 
-                  SILC_ID_CLIENT, client->id, (void *)client, TRUE);
+                  SILC_ID_CLIENT, client->id, (void *)client, TRUE, FALSE);
 
   nidp = silc_id_payload_encode(client->id, SILC_ID_CLIENT);
 
-  /* Send NICK_CHANGE notify */
+  /* Send NICK_CHANGE notify to the client's channels */
   silc_server_send_notify_on_channels(server, client, 
                                      SILC_NOTIFY_TYPE_NICK_CHANGE, 2,
                                      oidp->data, oidp->len, 
@@ -1309,14 +1737,23 @@ SILC_SERVER_CMD_FUNC(topic)
     goto out;
   }
   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;
+  }
 
   /* 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);
-    goto out;
+    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;
+    }
   }
 
   if (argc > 1) {
@@ -1336,15 +1773,15 @@ SILC_SERVER_CMD_FUNC(topic)
 
     /* 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;
-       }
+    while ((chl = silc_list_get(channel->user_list)) != SILC_LIST_END)
+      if (chl->client == client)
+       break;
+
+    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;
       }
     }
 
@@ -1353,10 +1790,17 @@ SILC_SERVER_CMD_FUNC(topic)
       silc_free(channel->topic);
     channel->topic = strdup(tmp);
 
+    /* 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);
+
     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_server_send_notify_to_channel(server, NULL, channel, TRUE,
                                       SILC_NOTIFY_TYPE_TOPIC_SET, 2,
                                       idp->data, idp->len,
                                       channel->topic, strlen(channel->topic));
@@ -1411,6 +1855,11 @@ SILC_SERVER_CMD_FUNC(invite)
     goto out;
   }
   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;
+  }
 
   /* Get Channel ID */
   tmp = silc_argument_get_arg_type(cmd->args, 2, &len);
@@ -1420,14 +1869,23 @@ SILC_SERVER_CMD_FUNC(invite)
     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 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;
+    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 whether the sender of this command is on the channel. */
@@ -1476,7 +1934,8 @@ SILC_SERVER_CMD_FUNC(invite)
   sidp = silc_id_payload_encode(sender->id, SILC_ID_CLIENT);
 
   /* 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_server_send_notify_dest(server, dest_sock, FALSE, dest_id, 
+                              SILC_ID_CLIENT,
                               SILC_NOTIFY_TYPE_INVITE, 2, 
                               sidp->data, sidp->len, tmp, len);
 
@@ -1490,20 +1949,30 @@ SILC_SERVER_CMD_FUNC(invite)
   silc_server_command_free(cmd);
 }
 
+typedef struct {
+  SilcServer server;
+  SilcSocketConnection sock;
+  char *signoff;
+} *QuitInternal;
+
 /* 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)
 {
-  SilcServer server = (SilcServer)context;
-  SilcSocketConnection sock = server->sockets[fd];
+  QuitInternal q = (QuitInternal)context;
 
   /* 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);
+  silc_server_free_client_data(q->server, q->sock, q->sock->user_data,
+                              q->signoff);
+  q->sock->user_data = NULL;
 
   /* Close the connection on our side */
-  silc_server_close_connection(server, sock);
+  silc_server_close_connection(q->server, q->sock);
+
+  silc_free(q->signoff);
+  silc_free(q);
 }
 
 /* Quits SILC session. This is the normal way to disconnect client. */
@@ -1513,14 +1982,31 @@ 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_LOG_DEBUG(("Start"));
+  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, server,
-                    0, 300000, SILC_TASK_TIMEOUT, SILC_TASK_PRI_LOW);
+                    silc_server_command_quit_cb, (void *)q,
+                    0, 200000, SILC_TASK_TIMEOUT, SILC_TASK_PRI_LOW);
 
+ out:
   silc_server_command_free(cmd);
 }
 
@@ -1587,10 +2073,6 @@ SILC_SERVER_CMD_FUNC(info)
   silc_server_command_free(cmd);
 }
 
-SILC_SERVER_CMD_FUNC(connect)
-{
-}
-
 /* Server side of command PING. This just replies to the ping. */
 
 SILC_SERVER_CMD_FUNC(ping)
@@ -1610,7 +2092,7 @@ SILC_SERVER_CMD_FUNC(ping)
                                          SILC_STATUS_ERR_NO_SERVER_ID);
     goto out;
   }
-  id = silc_id_str2id(tmp, SILC_ID_SERVER);
+  id = silc_id_str2id(tmp, len, SILC_ID_SERVER);
   if (!id)
     goto out;
 
@@ -1630,61 +2112,6 @@ SILC_SERVER_CMD_FUNC(ping)
   silc_server_command_free(cmd);
 }
 
-SILC_SERVER_CMD_FUNC(oper)
-{
-}
-
-/* Assembles USERS command and executes it. This is called when client
-   joins to a channel and we wan't to send USERS command reply to the 
-   client. */
-
-void silc_server_command_send_users(SilcServer server,
-                                   SilcSocketConnection sock,
-                                   SilcChannelEntry channel,
-                                   int pending)
-{
-  SilcServerCommandContext cmd;
-  SilcBuffer buffer, idp;
-  SilcPacketContext *packet = silc_packet_context_alloc();
-
-  /* Create USERS command packet and process it. */
-  idp = silc_id_payload_encode(channel->id, SILC_ID_CHANNEL);
-  buffer = silc_command_payload_encode_va(SILC_COMMAND_USERS, 0, 1,
-                                         1, idp->data, idp->len);
-
-  packet->buffer = silc_buffer_copy(buffer);
-  packet->sock = sock;
-  packet->type = SILC_PACKET_COMMAND;
-
-  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->packet = silc_packet_context_dup(packet);
-  cmd->pending = FALSE;
-
-  if (pending) {
-    /* If this function was called from pending command then instead of
-       processing the command now, register a pending command callback which
-       will process it after we've received the automatic USERS command 
-       reply. */
-    silc_server_command_pending(server, SILC_COMMAND_USERS, 0,
-                               silc_server_command_users, (void *)cmd);
-    cmd->pending = TRUE;
-    silc_free(buffer);
-    silc_free(idp);
-    return;
-  }
-
-  /* Process USERS command. */
-  silc_server_command_users((void *)cmd);
-  silc_free(buffer);
-  silc_free(idp);
-  silc_packet_context_free(packet);
-}
-
 /* 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. */
@@ -1698,11 +2125,11 @@ static void silc_server_command_join_channel(SilcServer server,
 {
   SilcSocketConnection sock = cmd->sock;
   unsigned char *tmp;
-  unsigned int tmp_len;
-  unsigned char *passphrase = NULL, mode[4], tmp2[4];
+  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;
+  SilcBuffer reply, chidp, clidp, keyp, user_list, mode_list;
   unsigned short ident = silc_command_get_ident(cmd->payload);
 
   SILC_LOG_DEBUG(("Start"));
@@ -1785,14 +2212,15 @@ static void silc_server_command_join_channel(SilcServer server,
   }
 
   /* Generate new channel key as protocol dictates */
-  if (!created || !channel->channel_key)
+  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, channel, 
+  silc_server_send_channel_key(server, NULL, channel, 
                               server->server_type == SILC_ROUTER ? 
-                              FALSE : server->standalone);
+                              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-
@@ -1804,6 +2232,10 @@ static void silc_server_command_join_channel(SilcServer server,
   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);
 
@@ -1811,34 +2243,46 @@ static void silc_server_command_join_channel(SilcServer server,
   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, 
-                                        SILC_ID_CHANNEL_LEN,
+                                        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, 5,
+                                          SILC_STATUS_OK, ident, 9,
                                           2, channel->channel_name,
                                           strlen(channel->channel_name),
                                           3, chidp->data, chidp->len,
-                                          4, mode, 4,
-                                          5, tmp2, 4,
-                                          6, keyp->data, keyp->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, 6
+                                          SILC_STATUS_OK, ident, 10
                                           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));
+                                          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 */
@@ -1847,27 +2291,25 @@ static void silc_server_command_join_channel(SilcServer server,
 
   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);
+    silc_server_send_notify_to_channel(server, NULL, channel, FALSE,
+                                      SILC_NOTIFY_TYPE_JOIN, 2,
+                                      clidp->data, clidp->len,
+                                      chidp->data, chidp->len);
 
-    /* Send NEW_CHANNEL_USER packet to our primary router */
+    /* Send JOIN notify packet to our primary router */
     if (!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);
+      silc_server_send_notify_join(server, server->router->connection,
+                                  server->server_type == SILC_ROUTER ?
+                                  TRUE : FALSE, channel, client->id,
+                                  SILC_ID_CLIENT_LEN);
   }
 
-  /* Send USERS command reply to the joined channel so the user sees who
-     is currently on the channel. */
-  silc_server_command_send_users(server, sock, channel, cmd->pending);
-
   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)
@@ -1882,7 +2324,7 @@ SILC_SERVER_CMD_FUNC(join)
   SilcServerCommandContext cmd = (SilcServerCommandContext)context;
   SilcServer server = cmd->server;
   int tmp_len;
-  char *tmp, *channel_name = NULL, *cipher = NULL;
+  char *tmp, *channel_name = NULL, *cipher, *hmac;
   SilcChannelEntry channel;
   unsigned int umode = 0;
   int created = FALSE;
@@ -1899,6 +2341,9 @@ SILC_SERVER_CMD_FUNC(join)
   }
   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);
@@ -1914,9 +2359,15 @@ SILC_SERVER_CMD_FUNC(join)
     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 name */
+  /* 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, 
@@ -1941,7 +2392,7 @@ SILC_SERVER_CMD_FUNC(join)
         the channel by ourselves. */
       if (server->standalone) {
        channel = silc_server_create_new_channel(server, server->id, cipher, 
-                                                channel_name);
+                                                hmac, channel_name, TRUE);
        umode = (SILC_CHANNEL_UMODE_CHANOP | SILC_CHANNEL_UMODE_CHANFO);
        created = TRUE;
 
@@ -1968,7 +2419,9 @@ SILC_SERVER_CMD_FUNC(join)
          /* Reprocess this packet after received reply from router */
          silc_server_command_pending(server, SILC_COMMAND_JOIN, 
                                      silc_command_get_ident(cmd->payload),
-                                     silc_server_command_join, context);
+                                     silc_server_command_destructor,
+                                     silc_server_command_join,
+                                     silc_server_command_dup(cmd));
          cmd->pending = TRUE;
          return;
        }
@@ -1980,7 +2433,7 @@ SILC_SERVER_CMD_FUNC(join)
        if (!channel) {
          /* Channel really does not exist, create it */
          channel = silc_server_create_new_channel(server, server->id, cipher, 
-                                                  channel_name);
+                                                  hmac, channel_name, TRUE);
          umode = (SILC_CHANNEL_UMODE_CHANOP | SILC_CHANNEL_UMODE_CHANFO);
          created = TRUE;
        }
@@ -2004,7 +2457,7 @@ SILC_SERVER_CMD_FUNC(join)
       if (!channel) {
        /* Channel really does not exist, create it */
        channel = silc_server_create_new_channel(server, server->id, cipher, 
-                                                channel_name);
+                                                hmac, channel_name, TRUE);
        umode = (SILC_CHANNEL_UMODE_CHANOP | SILC_CHANNEL_UMODE_CHANFO);
        created = TRUE;
       }
@@ -2161,6 +2614,11 @@ SILC_SERVER_CMD_FUNC(cmode)
     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);
@@ -2174,10 +2632,14 @@ SILC_SERVER_CMD_FUNC(cmode)
   /* 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);
-    goto out;
+  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 */
@@ -2259,7 +2721,7 @@ SILC_SERVER_CMD_FUNC(cmode)
     if (!tmp) {
       if (!(channel->mode & SILC_CHANNEL_MODE_ULIMIT)) {
        silc_server_command_send_status_reply(cmd, SILC_COMMAND_CMODE,
-                                             SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
+                                  SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
        goto out;
       }
     } else {
@@ -2280,7 +2742,7 @@ SILC_SERVER_CMD_FUNC(cmode)
       tmp = silc_argument_get_arg_type(cmd->args, 4, NULL);
       if (!tmp) {
        silc_server_command_send_status_reply(cmd, SILC_COMMAND_CMODE,
-                                             SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
+                                  SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
        goto out;
       }
 
@@ -2305,7 +2767,7 @@ SILC_SERVER_CMD_FUNC(cmode)
       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);
+                                  SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
        goto out;
       }
 
@@ -2332,7 +2794,7 @@ SILC_SERVER_CMD_FUNC(cmode)
       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);
+                                  SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
        goto out;
       }
 
@@ -2352,32 +2814,26 @@ SILC_SERVER_CMD_FUNC(cmode)
   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;
+      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);
+                                  SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
        goto out;
       }
 
-      cp = strchr(tmp, ':');
-      if (cp) {
-       key_len = atoi(cp);
-       *cp = '\0';
-      }
-
       /* 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);
-
-      key_len /= 8;
-      if (key_len > 32)
-       key_len = 32;
+      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);
@@ -2427,9 +2883,14 @@ SILC_SERVER_CMD_FUNC(cmode)
       /* 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_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);
@@ -2468,11 +2929,17 @@ SILC_SERVER_CMD_FUNC(cmode)
 
   /* Send CMODE_CHANGE notify */
   cidp = silc_id_payload_encode(client->id, SILC_ID_CLIENT);
-  silc_server_send_notify_to_channel(server, channel, TRUE,
+  silc_server_send_notify_to_channel(server, NULL, channel, FALSE,
                                     SILC_NOTIFY_TYPE_CMODE_CHANGE, 2,
                                     cidp->data, cidp->len, 
                                     tmp_mask, tmp_len);
-  silc_free(cidp);
+
+  /* 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,
@@ -2483,6 +2950,7 @@ SILC_SERVER_CMD_FUNC(cmode)
     
   silc_buffer_free(packet);
   silc_free(channel_id);
+  silc_free(cidp);
 
  out:
   silc_server_command_free(cmd);
@@ -2501,28 +2969,37 @@ SILC_SERVER_CMD_FUNC(cumode)
   SilcClientEntry target_client;
   SilcChannelClientEntry chl;
   SilcBuffer packet, idp;
-  unsigned char *tmp_id, *tmp_mask;
-  unsigned int target_mask, sender_mask, tmp_len;
+  unsigned char *tmp_id, *tmp_ch_id, *tmp_mask;
+  unsigned int target_mask, sender_mask, tmp_len, tmp_ch_len;
   int notify = FALSE;
 
   SILC_SERVER_COMMAND_CHECK_ARGC(SILC_COMMAND_CUMODE, cmd, 3, 3);
 
   /* Get Channel ID */
-  tmp_id = silc_argument_get_arg_type(cmd->args, 1, &tmp_len);
-  if (!tmp_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;
+  }
+  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;
   }
-  channel_id = silc_id_payload_parse_id(tmp_id, tmp_len);
 
   /* 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);
-    goto out;
+    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 */
@@ -2561,16 +3038,22 @@ SILC_SERVER_CMD_FUNC(cumode)
   tmp_id = silc_argument_get_arg_type(cmd->args, 3, &tmp_len);
   if (!tmp_id) {
     silc_server_command_send_status_reply(cmd, SILC_COMMAND_CUMODE,
-                                         SILC_STATUS_ERR_NO_CHANNEL_ID);
+                                         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;
+  }
 
   /* 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 */
+    target_client = silc_idlist_find_client_by_id(server->global_list, 
+                                                 client_id, NULL);
   }
 
   /* Check whether target client is on the channel */
@@ -2631,14 +3114,25 @@ SILC_SERVER_CMD_FUNC(cumode)
     }
   }
 
+  idp = silc_id_payload_encode(client->id, SILC_ID_CLIENT);
+
   /* 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_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);
-    silc_buffer_free(idp);
+                                      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 */
@@ -2652,6 +3146,7 @@ SILC_SERVER_CMD_FUNC(cumode)
   silc_buffer_free(packet);
   silc_free(channel_id);
   silc_free(client_id);
+  silc_buffer_free(idp);
 
  out:
   silc_server_command_free(cmd);
@@ -2661,24 +3156,316 @@ SILC_SERVER_CMD_FUNC(cumode)
 
 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;
+  }
+
+  /* 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;
+    }
+  }
+
+  /* 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;
+  }
+
+  /* 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;
+      }
+      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;
+  }
+
+  /* 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 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;
+      }
+      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;
+  }
+
+  /* Get comment */
+  tmp_len = 0;
+  comment = silc_argument_get_arg_type(cmd->args, 3, &tmp_len);
+  if (tmp_len > 128)
+    comment = NULL;
+
+  /* Send command reply to sender */
+  silc_server_command_send_status_reply(cmd, SILC_COMMAND_KICK, 
+                                       SILC_STATUS_OK);
+
+  /* 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);
+
+  /* Remove the client from the channel. If the channel does not exist
+     after removing the client then the client kicked itself of 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;
+
+  /* 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, 0);
+
+  /* Send the channel key to the channel. The key of course is not sent
+     to the client who joined the channel. */
+  silc_server_send_channel_key(server, target_client->connection, channel, 
+                              server->server_type == SILC_ROUTER ? 
+                              FALSE : !server->standalone);
+
+ out:
+  silc_server_command_free(cmd);
+}
+
+SILC_SERVER_CMD_FUNC(oper)
+{
+}
+
+SILC_SERVER_CMD_FUNC(silcoper)
+{
+}
+
+/* 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;
+  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;
+
+  /* Check whether client has the permissions. */
+  if (client->mode == SILC_UMODE_NONE) {
+    silc_server_command_send_status_reply(cmd, SILC_COMMAND_CONNECT,
+                                         SILC_STATUS_ERR_NO_SERVER_PRIV);
+    goto out;
+  }
+
+  if (server->server_type == SILC_ROUTER && 
+      client->mode & SILC_UMODE_SERVER_OPERATOR) {
+    silc_server_command_send_status_reply(cmd, SILC_COMMAND_CONNECT,
+                                         SILC_STATUS_ERR_NO_ROUTER_PRIV);
+    goto out;
+  }
+
+  /* Get the remote server */
+  tmp = silc_argument_get_arg_type(cmd->args, 1, &tmp_len);
+  if (!tmp) {
+    silc_server_command_send_status_reply(cmd, SILC_COMMAND_CONNECT,
+                                         SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
+    goto out;
+  }
+
+  /* Get port */
+  tmp = silc_argument_get_arg_type(cmd->args, 2, &tmp_len);
+  if (tmp)
+    SILC_GET32_MSB(port, tmp);
+
+  /* Create the connection. It is done with timeout and is async. */
+  silc_server_create_connection(server, tmp, port);
+
+  /* Send reply to the sender */
+  silc_server_command_send_status_reply(cmd, SILC_COMMAND_CONNECT,
+                                       SILC_STATUS_OK);
+
+ out:
+  silc_server_command_free(cmd);
 }
 
 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;
+  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;
+  }
+
+  /* Close the connection to the server */
+  silc_server_free_sock_user_data(server, server_entry->connection);
+  silc_server_disconnect_remote(server, server_entry->connection,
+                               "Server closed connection: "
+                               "Closed by operator");
+  
+  /* Send reply to the sender */
+  silc_server_command_send_status_reply(cmd, SILC_COMMAND_CLOSE,
+                                       SILC_STATUS_OK);
+
+ 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;
+  }
+
+  /* Then, gracefully, or not, bring the server down. */
+  silc_server_stop(server);
+
+  /* Send reply to the sender */
+  silc_server_command_send_status_reply(cmd, SILC_COMMAND_SHUTDOWN,
+                                       SILC_STATUS_OK);
+
+ 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)
@@ -2703,13 +3490,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 */
@@ -2720,12 +3515,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,
@@ -2755,7 +3550,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 {
 
   }
@@ -2781,19 +3576,17 @@ SILC_SERVER_CMD_FUNC(users)
   SilcServerCommandContext cmd = (SilcServerCommandContext)context;
   SilcServer server = cmd->server;
   SilcChannelEntry channel;
-  SilcChannelClientEntry chl;
   SilcChannelID *id;
   SilcBuffer packet;
   unsigned char *channel_id;
   unsigned int channel_id_len;
   SilcBuffer client_id_list;
   SilcBuffer client_mode_list;
-  SilcBuffer idp;
   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_USERS, cmd, 1, 2);
+  SILC_SERVER_COMMAND_CHECK_ARGC(SILC_COMMAND_USERS, cmd, 1, 1);
 
   /* Get Channel ID */
   channel_id = silc_argument_get_arg_type(cmd->args, 1, &channel_id_len);
@@ -2803,6 +3596,11 @@ SILC_SERVER_CMD_FUNC(users)
     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;
+  }
 
   /* 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
@@ -2824,7 +3622,9 @@ SILC_SERVER_CMD_FUNC(users)
       /* Reprocess this packet after received reply */
       silc_server_command_pending(server, SILC_COMMAND_USERS, 
                                  silc_command_get_ident(cmd->payload),
-                                 silc_server_command_users, (void *)cmd);
+                                 silc_server_command_destructor,
+                                 silc_server_command_users,
+                                 silc_server_command_dup(cmd));
       cmd->pending = TRUE;
       silc_command_set_ident(cmd->payload, ident);
       
@@ -2843,33 +3643,9 @@ SILC_SERVER_CMD_FUNC(users)
     }
   }
 
-  /* Assemble the lists 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));
-  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) {
-    /* Client ID */
-    idp = silc_id_payload_encode(chl->client->id, SILC_ID_CLIENT);
-    silc_buffer_put(client_id_list, idp->data, idp->len);
-    silc_buffer_pull(client_id_list, idp->len);
-    silc_buffer_free(idp);
-
-    /* Client's mode on channel */
-    SILC_PUT32_MSB(chl->mode, client_mode_list->data);
-    silc_buffer_pull(client_mode_list, 4);
-
-    list_count++;
-  }
-  silc_buffer_push(client_id_list, 
-                  client_id_list->data - client_id_list->head);
-  silc_buffer_push(client_mode_list, 
-                  client_mode_list->data - client_mode_list->head);
+  /* Get the users list */
+  silc_server_get_users_on_channel(server, channel, &client_id_list,
+                                  &client_mode_list, &list_count);
 
   /* List count */
   SILC_PUT32_MSB(list_count, lc);