updates
[silc.git] / apps / silcd / command.c
index d937dd36822c3aad7c0f0b83777d910d26163038..c951889e53f0e37377bf1678c14fba17ef040a36 100644 (file)
@@ -38,6 +38,9 @@ silc_server_command_send_status_data(SilcServerCommandContext cmd,
                                     unsigned char *arg,
                                     unsigned int arg_len);
 static void silc_server_command_free(SilcServerCommandContext cmd);
+void silc_server_command_send_names(SilcServer server,
+                                   SilcSocketConnection sock,
+                                   SilcChannelEntry channel);
 
 /* Server command list. */
 SilcServerCommand silc_command_list[] =
@@ -75,8 +78,25 @@ SilcServerCommand silc_command_list[] =
   { NULL, 0 },
 };
 
-/* List of pending commands. */
-SilcDList silc_command_pending;
+#define SILC_SERVER_COMMAND_CHECK_ARGC(command, context, min, max)           \
+do {                                                                         \
+  unsigned int _argc = silc_argument_get_arg_num(cmd->args);                 \
+                                                                             \
+  SILC_LOG_DEBUG(("Start"));                                                 \
+                                                                             \
+  if (_argc < min) {                                                         \
+    silc_server_command_send_status_reply(cmd, command,                              \
+                                         SILC_STATUS_ERR_NOT_ENOUGH_PARAMS); \
+    silc_server_command_free(cmd);                                           \
+    return;                                                                  \
+  }                                                                          \
+  if (_argc > max) {                                                         \
+    silc_server_command_send_status_reply(cmd, command,                              \
+                                         SILC_STATUS_ERR_TOO_MANY_PARAMS);   \
+    silc_server_command_free(cmd);                                           \
+    return;                                                                  \
+  }                                                                          \
+} while(0)
 
 /* Returns TRUE if the connection is registered. Unregistered connections
    usually cannot send commands hence the check. */
@@ -116,12 +136,12 @@ void silc_server_command_process(SilcServer server,
     SilcClientEntry client = (SilcClientEntry)sock->user_data;
 
     if (!client)
-      goto out;
+      return;
 
     /* Allow only one command executed in 2 seconds. */
     curtime = time(NULL);
     if (client->last_command && (curtime - client->last_command) < 2)
-      goto out;
+      return;
 
     /* Update access time */
     client->last_command = curtime;
@@ -133,13 +153,14 @@ void silc_server_command_process(SilcServer server,
   ctx = silc_calloc(1, sizeof(*ctx));
   ctx->server = server;
   ctx->sock = sock;
-  ctx->packet = packet;        /* Save original packet */
+  ctx->packet = silc_packet_context_dup(packet); /* Save original packet */
   
   /* Parse the command payload in the packet */
   ctx->payload = silc_command_payload_parse(packet->buffer);
   if (!ctx->payload) {
     SILC_LOG_ERROR(("Bad command payload, packet dropped"));
     silc_buffer_free(packet->buffer);
+    silc_packet_context_free(packet);
     silc_free(ctx);
     return;
   }
@@ -162,12 +183,9 @@ void silc_server_command_process(SilcServer server,
 
   if (cmd == NULL) {
     SILC_LOG_ERROR(("Unknown command, packet dropped"));
-    silc_free(ctx);
-    goto out;
+    silc_server_command_free(ctx);
+    return;
   }
-
- out:
-  silc_buffer_free(packet->buffer);
 }
 
 /* Add new pending command to be executed when reply to a command has been
@@ -176,7 +194,8 @@ void silc_server_command_process(SilcServer server,
    the `callback' will be executed when received reply with command
    identifier `ident'. */
 
-void silc_server_command_pending(SilcCommand reply_cmd,
+void silc_server_command_pending(SilcServer server,
+                                SilcCommand reply_cmd,
                                 unsigned short ident,
                                 SilcCommandCb callback,
                                 void *context)
@@ -188,19 +207,21 @@ void silc_server_command_pending(SilcCommand reply_cmd,
   reply->ident = ident;
   reply->context = context;
   reply->callback = callback;
-  silc_dlist_add(silc_command_pending, reply);
+  silc_dlist_add(server->pending_commands, reply);
 }
 
 /* Deletes pending command by reply command type. */
 
-void silc_server_command_pending_del(SilcCommand reply_cmd,
+void silc_server_command_pending_del(SilcServer server,
+                                    SilcCommand reply_cmd,
                                     unsigned short ident)
 {
   SilcServerCommandPending *r;
 
-  while ((r = silc_dlist_get(silc_command_pending)) != SILC_LIST_END) {
+  silc_dlist_start(server->pending_commands);
+  while ((r = silc_dlist_get(server->pending_commands)) != SILC_LIST_END) {
     if (r->reply_cmd == reply_cmd && r->ident == ident) {
-      silc_dlist_del(silc_command_pending, r);
+      silc_dlist_del(server->pending_commands, r);
       break;
     }
   }
@@ -209,13 +230,15 @@ void silc_server_command_pending_del(SilcCommand reply_cmd,
 /* Checks for pending commands and marks callbacks to be called from
    the command reply function. Returns TRUE if there were pending command. */
 
-int silc_server_command_pending_check(SilcServerCommandReplyContext ctx,
+int silc_server_command_pending_check(SilcServer server,
+                                     SilcServerCommandReplyContext ctx,
                                      SilcCommand command, 
                                      unsigned short ident)
 {
   SilcServerCommandPending *r;
 
-  while ((r = silc_dlist_get(silc_command_pending)) != SILC_LIST_END) {
+  silc_dlist_start(server->pending_commands);
+  while ((r = silc_dlist_get(server->pending_commands)) != SILC_LIST_END) {
     if (r->reply_cmd == command && r->ident == ident) {
       ctx->context = r->context;
       ctx->callback = r->callback;
@@ -232,7 +255,10 @@ int silc_server_command_pending_check(SilcServerCommandReplyContext ctx,
 static void silc_server_command_free(SilcServerCommandContext cmd)
 {
   if (cmd) {
-    silc_command_free_payload(cmd->payload);
+    if (cmd->payload)
+      silc_command_free_payload(cmd->payload);
+    if (cmd->packet)
+      silc_packet_context_free(cmd->packet);
     silc_free(cmd);
   }
 }
@@ -278,60 +304,46 @@ silc_server_command_send_status_data(SilcServerCommandContext cmd,
   silc_buffer_free(buffer);
 }
 
-/* Server side of command WHOIS. Processes user's query and sends found 
-   results as command replies back to the client. */
+/******************************************************************************
 
-SILC_SERVER_CMD_FUNC(whois)
-{
-  SilcServerCommandContext cmd = (SilcServerCommandContext)context;
-  SilcServer server = cmd->server;
-  char *tmp, *nick = NULL, *server_name = NULL;
-  unsigned int i, argc, count = 0, len, clients_count;
-  int use_id = FALSE;
-  SilcClientID *client_id = NULL;
-  SilcBuffer packet, idp;
-  SilcClientEntry *clients = NULL, entry;
-  SilcCommandStatus status;
+                              WHOIS Functions
 
-  SILC_LOG_DEBUG(("Start"));
+******************************************************************************/
 
-  argc = silc_argument_get_arg_num(cmd->args);
-  if (argc < 1) {
-    silc_server_command_send_status_reply(cmd, SILC_COMMAND_WHOIS,
-                                         SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
-    goto out;
-  }
-  if (argc > 3) {
-    silc_server_command_send_status_reply(cmd, SILC_COMMAND_WHOIS,
-                                         SILC_STATUS_ERR_TOO_MANY_PARAMS);
-    goto out;
-  }
+static int
+silc_server_command_whois_parse(SilcServerCommandContext cmd,
+                               SilcClientID **client_id,
+                               char **nickname,
+                               char **server_name,
+                               int *count)
+{
+  unsigned char *tmp;
+  unsigned int len;
+  unsigned int argc = silc_argument_get_arg_num(cmd->args);
 
   /* If client ID is in the command it must be used instead of nickname */
   tmp = silc_argument_get_arg_type(cmd->args, 2, &len);
   if (!tmp) {
-
     /* No ID, get the nickname@server string and parse it. */
     tmp = silc_argument_get_arg_type(cmd->args, 1, NULL);
     if (tmp) {
       if (strchr(tmp, '@')) {
        len = strcspn(tmp, "@");
-       nick = silc_calloc(len + 1, sizeof(char));
-       memcpy(nick, tmp, len);
-       server_name = silc_calloc(strlen(tmp) - len, sizeof(char));
-       memcpy(server_name, tmp + len + 1, strlen(tmp) - len - 1);
+       *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 {
-       nick = strdup(tmp);
+       *nickname = strdup(tmp);
       }
     } else {
       silc_server_command_send_status_reply(cmd, SILC_COMMAND_WHOIS,
                                            SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
-      goto out;
+      return FALSE;
     }
   } else {
     /* Command includes ID, use that */
-    client_id = silc_id_payload_parse_id(tmp, len);
-    use_id = TRUE;
+    *client_id = silc_id_payload_parse_id(tmp, len);
   }
 
   /* Get the max count of reply messages allowed */
@@ -340,74 +352,73 @@ SILC_SERVER_CMD_FUNC(whois)
     if (!tmp) {
       silc_server_command_send_status_reply(cmd, SILC_COMMAND_WHOIS,
                                            SILC_STATUS_ERR_TOO_MANY_PARAMS);
-      if (nick)
-       silc_free(nick);
-      if (server_name)
-       silc_free(server_name);
-      goto out;
+      if (*nickname)
+       silc_free(*nickname);
+      if (*server_name)
+       silc_free(*server_name);
+
+      return FALSE;
     }
-    count = atoi(tmp);
+    *count = atoi(tmp);
   }
 
-  /* Protocol dictates that we must always send the received WHOIS request
-     to our router if we are normal server, so let's do it now unless we
-     are standalone. We will not send any replies to the client until we
-     have received reply from the router. */
-  if (!server->standalone) {
-    SilcBuffer tmpbuf;
+  return TRUE;
+}
 
-    silc_command_set_ident(cmd->payload, silc_rng_get_rn16(server->rng));
-    tmpbuf = silc_command_payload_encode_payload(cmd->payload);
+static char
+silc_server_command_whois_check(SilcServerCommandContext cmd,
+                               SilcClientEntry *clients,
+                               unsigned int clients_count)
+{
+  SilcServer server = cmd->server;
+  int i;
+  SilcClientEntry entry;
 
-    /* Send WHOIS command to our router */
-    silc_server_packet_send(server, (SilcSocketConnection)
-                           server->id_entry->router->connection,
-                           SILC_PACKET_COMMAND, cmd->packet->flags,
-                           tmpbuf->data, tmpbuf->len, TRUE);
-    return;
-  } else {
-    /* We are standalone, let's just do local search and send reply to
-       requesting client. */
+  for (i = 0; i < clients_count; i++) {
+    entry = clients[i];
 
-    /* Get all clients matching that nickname */
-    if (!use_id) {
-      clients = silc_idlist_get_clients_by_nickname(server->local_list, 
-                                                   nick, server_name,
-                                                   &clients_count);
-    } else {
-      entry = silc_idlist_find_client_by_id(server->local_list, client_id);
-      if (entry) {
-       clients = silc_calloc(1, sizeof(*clients));
-       clients[0] = entry;
-       clients_count = 1;
-      }
-    }
-    
-    /* If we are router then we will check our global list as well. */
-    if (server->server_type == SILC_ROUTER) {
-      entry =
-       silc_idlist_find_client_by_nickname(server->global_list,
-                                           nick, server_name);
-      if (!entry) {
-       silc_server_command_send_status_data(cmd, SILC_COMMAND_WHOIS,
-                                            SILC_STATUS_ERR_NO_SUCH_NICK,
-                                            3, tmp, strlen(tmp));
-       goto out;
-      }
-      goto ok;
-    }
+    if (!entry->nickname || !entry->username || !entry->userinfo) {
+      SilcBuffer tmpbuf;
+      unsigned short old_ident;
       
-#if 0
-    silc_server_command_send_status_data(cmd, SILC_COMMAND_WHOIS,
-                                        SILC_STATUS_ERR_NO_SUCH_NICK,
-                                        3, tmp, strlen(tmp));
-    goto out;
-#endif
+      old_ident = silc_command_get_ident(cmd->payload);
+      silc_command_set_ident(cmd->payload, silc_rng_get_rn16(server->rng));
+      tmpbuf = silc_command_payload_encode_payload(cmd->payload);
+
+      /* Send WHOIS command */
+      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_whois, (void *)cmd);
+      cmd->pending = TRUE;
+      
+      silc_command_set_ident(cmd->payload, old_ident);
+
+      silc_buffer_free(tmpbuf);
+      return FALSE;
+    }
   }
 
-  /* We are standalone and will send reply to client */
- ok:
+  return TRUE;
+}
+
+static void
+silc_server_command_whois_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;
@@ -429,7 +440,7 @@ SILC_SERVER_CMD_FUNC(whois)
     tmp = silc_argument_get_first_arg(cmd->args, NULL);
     
     /* XXX */
-    if (cmd->sock->type == SILC_SOCKET_TYPE_CLIENT) {
+    {
       char nh[256], uh[256];
       unsigned char idle[4];
       SilcSocketConnection hsock;
@@ -438,17 +449,21 @@ SILC_SERVER_CMD_FUNC(whois)
       memset(nh, 0, sizeof(nh));
       
       strncat(nh, entry->nickname, strlen(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 (!strchr(entry->nickname, '@')) {
+       strncat(nh, "@", 1);
+       len = entry->router ? strlen(entry->router->server_name) :
+         strlen(server->server_name);
+       strncat(nh, entry->router ? entry->router->server_name :
+               server->server_name, len);
+      }
       
       strncat(uh, entry->username, strlen(entry->username));
-      strncat(uh, "@", 1);
-      hsock = (SilcSocketConnection)entry->connection;
-      len = hsock->hostname ? strlen(hsock->hostname) : strlen(hsock->ip);
-      strncat(uh, hsock->hostname ? hsock->hostname : hsock->ip, len);
+      if (!strchr(entry->username, '@')) {
+       strncat(uh, "@", 1);
+       hsock = (SilcSocketConnection)entry->connection;
+       len = strlen(hsock->hostname);
+       strncat(uh, hsock->hostname, len);
+      }
       
       SILC_PUT32_MSB((time(NULL) - entry->data.last_receive), idle);
       
@@ -456,7 +471,7 @@ SILC_SERVER_CMD_FUNC(whois)
       if (entry->userinfo)
        packet = 
          silc_command_reply_payload_encode_va(SILC_COMMAND_WHOIS,
-                                              status, 0, 5, 
+                                              status, ident, 5, 
                                               2, idp->data, idp->len,
                                               3, nh, strlen(nh),
                                               4, uh, strlen(uh),
@@ -466,92 +481,291 @@ SILC_SERVER_CMD_FUNC(whois)
       else
        packet = 
          silc_command_reply_payload_encode_va(SILC_COMMAND_WHOIS,
-                                              status, 0, 4, 
+                                              status, ident, 4, 
                                               2, idp->data, idp->len,
                                               3, nh, strlen(nh),
                                               4, uh, strlen(uh),
                                               7, idle, 4);
-      
-    } else {
-      /* XXX */
-      packet = 
-       silc_command_reply_payload_encode_va(SILC_COMMAND_WHOIS, 
-                                            status, 0, 3, 
-                                            2, idp->data, idp->len,
-                                            3, entry->nickname, 
-                                            strlen(entry->nickname),
-                                            4, tmp, strlen(tmp)); /* XXX */
     }
+    
     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);
   }
+}
 
-  silc_free(clients);
+static int
+silc_server_command_whois_from_client(SilcServerCommandContext cmd)
+{
+  SilcServer server = cmd->server;
+  char *nick = NULL, *server_name = NULL;
+  int count = 0, clients_count;
+  SilcClientID *client_id = NULL;
+  SilcClientEntry *clients = NULL, entry;
+  int ret = 0;
 
-  if (client_id)
-    silc_free(client_id);
+  /* Parse the whois request */
+  if (!silc_server_command_whois_parse(cmd, &client_id, &nick, 
+                                      &server_name, &count))
+    return 0;
+
+  /* Protocol dictates that we must always send the received WHOIS request
+     to our router if we are normal server, so let's do it now unless we
+     are standalone. We will not send any replies to the client until we
+     have received reply from the router. */
+  if (server->server_type == SILC_SERVER && 
+      !cmd->pending && !server->standalone) {
+    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 WHOIS command to our router */
+    silc_server_packet_send(server, (SilcSocketConnection)
+                           server->router->connection,
+                           SILC_PACKET_COMMAND, cmd->packet->flags,
+                           tmpbuf->data, tmpbuf->len, TRUE);
+
+    /* Reprocess this packet after received reply from router */
+    silc_server_command_pending(server, SILC_COMMAND_WHOIS, 
+                               silc_command_get_ident(cmd->payload),
+                               silc_server_command_whois, (void *)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. */
+
+  /* Get all clients matching that ID or nickname from local list */
+  if (client_id) {
+    entry = silc_idlist_find_client_by_id(server->local_list, client_id, NULL);
+    if (entry) {
+      clients = silc_calloc(1, sizeof(*clients));
+      clients[0] = entry;
+      clients_count = 1;
+    }
+  } else {
+    clients = silc_idlist_get_clients_by_nickname(server->local_list, 
+                                                 nick, server_name,
+                                                 &clients_count);
+  }
+  
+  /* Check global list as well */
+  if (!clients) {
+    if (client_id) {
+      entry = silc_idlist_find_client_by_id(server->global_list, 
+                                           client_id, NULL);
+      if (entry) {
+       clients = silc_calloc(1, sizeof(*clients));
+       clients[0] = entry;
+       clients_count = 1;
+      }
+    } else {
+      clients = silc_idlist_get_clients_by_nickname(server->global_list, 
+                                                   nick, server_name,
+                                                   &clients_count);
+    }
+  }
+  
+  if (!clients) {
+    /* Such a client really does not exist in the SILC network. */
+    silc_server_command_send_status_data(cmd, SILC_COMMAND_WHOIS,
+                                        SILC_STATUS_ERR_NO_SUCH_NICK,
+                                        3, nick, strlen(nick));
+    goto out;
+  }
+
+  /* Router always finds the client entry if it exists in the SILC network.
+     However, it might be incomplete entry and does not include all the
+     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)) {
+    ret = -1;
+    goto out;
+  }
+
+  /* Send the command reply to the client */
+  silc_server_command_whois_send_reply(cmd, clients, clients_count);
 
  out:
-  silc_server_command_free(cmd);
-}
+  if (clients)
+    silc_free(clients);
+  if (client_id)
+    silc_free(client_id);
+  if (nick)
+    silc_free(nick);
+  if (server_name)
+    silc_free(server_name);
 
-SILC_SERVER_CMD_FUNC(whowas)
-{
+  return ret;
 }
 
-SILC_SERVER_CMD_FUNC(identify)
+static int
+silc_server_command_whois_from_server(SilcServerCommandContext cmd)
 {
-  SilcServerCommandContext cmd = (SilcServerCommandContext)context;
   SilcServer server = cmd->server;
-  char *tmp, *nick = NULL, *server_name = NULL;
-  unsigned int argc, count = 0, len;
-  int use_id = FALSE;
+  char *nick = NULL, *server_name = NULL;
+  int count = 0, clients_count;
   SilcClientID *client_id = NULL;
-  SilcClientEntry entry;
-  SilcBuffer packet, idp;
-
-  SILC_LOG_DEBUG(("Start"));
+  SilcClientEntry *clients = NULL, entry;
+  int ret = 0;
+
+  /* Parse the whois request */
+  if (!silc_server_command_whois_parse(cmd, &client_id, &nick, 
+                                      &server_name, &count))
+    return 0;
+
+  /* Process the command request. Let's search for the requested client and
+     send reply to the requesting server. */
+
+  if (client_id) {
+    entry = silc_idlist_find_client_by_id(server->local_list, client_id, NULL);
+    if (entry) {
+      clients = silc_calloc(1, sizeof(*clients));
+      clients[0] = entry;
+      clients_count = 1;
+    }
+  } else {
+    clients = silc_idlist_get_clients_by_nickname(server->local_list, 
+                                                 nick, server_name,
+                                                 &clients_count);
+    if (!clients)
+      clients = silc_idlist_get_clients_by_hash(server->local_list, 
+                                               nick, server->md5hash,
+                                               &clients_count);
+  }
+  
+  /* If we are router we will check our global list as well. */
+  if (!clients && server->server_type == SILC_ROUTER) {
+    if (client_id) {
+      entry = silc_idlist_find_client_by_id(server->global_list, 
+                                           client_id, NULL);
+      if (entry) {
+       clients = silc_calloc(1, sizeof(*clients));
+       clients[0] = entry;
+       clients_count = 1;
+      }
+    } else {
+      clients = silc_idlist_get_clients_by_nickname(server->global_list, 
+                                                   nick, server_name,
+                                                   &clients_count);
+      if (!clients)
+       clients = silc_idlist_get_clients_by_hash(server->global_list, 
+                                                 nick, server->md5hash,
+                                                 &clients_count);
+    }
+  }
 
-  argc = silc_argument_get_arg_num(cmd->args);
-  if (argc < 1) {
-    silc_server_command_send_status_reply(cmd, SILC_COMMAND_IDENTIFY,
-                                         SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
+  if (!clients) {
+    /* Such a client really does not exist in the SILC network. */
+    silc_server_command_send_status_data(cmd, SILC_COMMAND_WHOIS,
+                                        SILC_STATUS_ERR_NO_SUCH_NICK,
+                                        3, nick, strlen(nick));
     goto out;
   }
-  if (argc > 3) {
-    silc_server_command_send_status_reply(cmd, SILC_COMMAND_IDENTIFY,
-                                         SILC_STATUS_ERR_TOO_MANY_PARAMS);
+
+  /* Router always finds the client entry if it exists in the SILC network.
+     However, it might be incomplete entry and does not include all the
+     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)) {
+    ret = -1;
     goto out;
   }
 
+  /* Send the command reply to the client */
+  silc_server_command_whois_send_reply(cmd, clients, clients_count);
+
+ out:
+  if (clients)
+    silc_free(clients);
+  if (client_id)
+    silc_free(client_id);
+  if (nick)
+    silc_free(nick);
+  if (server_name)
+    silc_free(server_name);
+
+  return ret;
+}
+
+/* Server side of command WHOIS. Processes user's query and sends found 
+   results as command replies back to the client. */
+
+SILC_SERVER_CMD_FUNC(whois)
+{
+  SilcServerCommandContext cmd = (SilcServerCommandContext)context;
+  int ret;
+
+  SILC_SERVER_COMMAND_CHECK_ARGC(SILC_COMMAND_WHOIS, cmd, 1, 3);
+
+  if (cmd->sock->type == SILC_SOCKET_TYPE_CLIENT)
+    ret = silc_server_command_whois_from_client(cmd);
+  else
+    ret = silc_server_command_whois_from_server(cmd);
+
+  if (!ret)
+    silc_server_command_free(cmd);
+}
+
+SILC_SERVER_CMD_FUNC(whowas)
+{
+}
+
+/******************************************************************************
+
+                              IDENTIFY Functions
+
+******************************************************************************/
+
+static int
+silc_server_command_identify_parse(SilcServerCommandContext cmd,
+                                  SilcClientID **client_id,
+                                  char **nickname,
+                                  char **server_name,
+                                  int *count)
+{
+  unsigned char *tmp;
+  unsigned int len;
+  unsigned int argc = silc_argument_get_arg_num(cmd->args);
+
   /* If client ID is in the command it must be used instead of nickname */
   tmp = silc_argument_get_arg_type(cmd->args, 2, &len);
   if (!tmp) {
-
-    /* Get the nickname@server string and parse it. */
+    /* No ID, get the nickname@server string and parse it. */
     tmp = silc_argument_get_arg_type(cmd->args, 1, NULL);
     if (tmp) {
       if (strchr(tmp, '@')) {
        len = strcspn(tmp, "@");
-       nick = silc_calloc(len + 1, sizeof(char));
-       memcpy(nick, tmp, len);
-       server_name = silc_calloc(strlen(tmp) - len, sizeof(char));
-       memcpy(server_name, tmp + len + 1, strlen(tmp) - len - 1);
+       *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 {
-       nick = strdup(tmp);
+       *nickname = strdup(tmp);
       }
     } else {
       silc_server_command_send_status_reply(cmd, SILC_COMMAND_IDENTIFY,
                                            SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
-      goto out;
+      return FALSE;
     }
   } else {
     /* Command includes ID, use that */
-    client_id = silc_id_payload_parse_id(tmp, len);
-    use_id = TRUE;
+    *client_id = silc_id_payload_parse_id(tmp, len);
   }
 
   /* Get the max count of reply messages allowed */
@@ -560,84 +774,363 @@ SILC_SERVER_CMD_FUNC(identify)
     if (!tmp) {
       silc_server_command_send_status_reply(cmd, SILC_COMMAND_IDENTIFY,
                                            SILC_STATUS_ERR_TOO_MANY_PARAMS);
-      goto out;
+      if (*nickname)
+       silc_free(*nickname);
+      if (*server_name)
+       silc_free(*server_name);
+
+      return FALSE;
     }
-    count = atoi(tmp);
+    *count = atoi(tmp);
   }
 
-  /* Find client */
-  if (!use_id) {
-    entry = silc_idlist_find_client_by_nickname(server->local_list,
-                                               nick, NULL);
-    if (!entry)
-      entry = silc_idlist_find_client_by_hash(server->global_list,
-                                             nick, server->md5hash);
-  } else {
-    entry = silc_idlist_find_client_by_id(server->local_list, client_id);
+  return TRUE;
+}
+
+/* Checks that all mandatory fields are present. If not then send WHOIS 
+   request to the server who owns the client. We use WHOIS because we want
+   to get as much information as possible at once. */
+
+static char
+silc_server_command_identify_check(SilcServerCommandContext cmd,
+                                  SilcClientEntry *clients,
+                                  unsigned int clients_count)
+{
+  SilcServer server = cmd->server;
+  int i;
+  SilcClientEntry entry;
+
+  for (i = 0; i < clients_count; i++) {
+    entry = clients[i];
+
+    if (!entry->nickname) {
+      SilcBuffer tmpbuf;
+      unsigned short old_ident;
+      
+      old_ident = silc_command_get_ident(cmd->payload);
+      silc_command_set_ident(cmd->payload, silc_rng_get_rn16(server->rng));
+      silc_command_set_command(cmd->payload, SILC_COMMAND_WHOIS);
+      tmpbuf = silc_command_payload_encode_payload(cmd->payload);
+      
+      /* Send WHOIS request. We send WHOIS since we're doing the requesting
+        now anyway so make it a good one. */
+      silc_server_packet_send(server, entry->router->connection,
+                             SILC_PACKET_COMMAND, cmd->packet->flags,
+                             tmpbuf->data, tmpbuf->len, TRUE);
+      
+      /* Reprocess this packet after received reply */
+      silc_server_command_pending(server, SILC_COMMAND_WHOIS, 
+                                 silc_command_get_ident(cmd->payload),
+                                 silc_server_command_identify, (void *)cmd);
+      cmd->pending = TRUE;
+      
+      /* Put old data back to the Command Payload we just changed */
+      silc_command_set_ident(cmd->payload, old_ident);
+      silc_command_set_command(cmd->payload, SILC_COMMAND_IDENTIFY);
+
+      silc_buffer_free(tmpbuf);
+      return FALSE;
+    }
   }
 
-  /* If client was not found and if we are normal server and are connected
-     to a router we will make global query from the router. */
-  if (!entry && server->server_type == SILC_SERVER && !server->standalone &&
-      !cmd->pending) {
-    SilcBuffer buffer = cmd->packet->buffer;
-    
-    SILC_LOG_DEBUG(("Requesting identify from router"));
+  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 (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,
+                                                     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));
+      }
+      
+      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;
+  SilcClientID *client_id = NULL;
+  SilcClientEntry *clients = NULL, entry;
+  int ret = 0;
+
+  /* Parse the IDENTIFY request */
+  if (!silc_server_command_identify_parse(cmd, &client_id, &nick, 
+                                         &server_name, &count))
+    return 0;
+
+  /* Protocol dictates that we must always send the received IDENTIFY request
+     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_buffer_push(buffer, buffer->data - buffer->head);
-    silc_server_packet_forward(server, (SilcSocketConnection)
-                              server->id_entry->router->connection,
-                              buffer->data, buffer->len, TRUE);
-    return;
+    silc_server_packet_send(server, (SilcSocketConnection)
+                           server->router->connection,
+                           SILC_PACKET_COMMAND, cmd->packet->flags,
+                           tmpbuf->data, tmpbuf->len, TRUE);
+
+    /* Reprocess this packet after received reply from router */
+    silc_server_command_pending(server, SILC_COMMAND_IDENTIFY, 
+                               silc_command_get_ident(cmd->payload),
+                               silc_server_command_identify, (void *)cmd);
+    cmd->pending = TRUE;
+
+    silc_command_set_ident(cmd->payload, old_ident);
+
+    silc_buffer_free(tmpbuf);
+    ret = -1;
+    goto out;
   }
 
-  /* If we are router we have checked our local list by nickname and our
-     global list by hash so far. It is possible that the client is still not
-     found and we'll check it from local list by hash. */
-  if (!entry && server->server_type == SILC_ROUTER)
-    entry = silc_idlist_find_client_by_hash(server->local_list,
-                                           nick, server->md5hash);
+  /* We are ready to process the command request. Let's search for the
+     requested client and send reply to the requesting client. */
 
-  if (!entry) {
-    /* The client definitely does not exist */
+  /* Get all clients matching that ID or nickname from local list */
+  if (client_id) {
+    entry = silc_idlist_find_client_by_id(server->local_list, client_id, NULL);
+    if (entry) {
+      clients = silc_calloc(1, sizeof(*clients));
+      clients[0] = entry;
+      clients_count = 1;
+    }
+  } else {
+    clients = silc_idlist_get_clients_by_nickname(server->local_list, 
+                                                 nick, server_name,
+                                                 &clients_count);
+  }
+  
+  /* Check global list as well */
+  if (!clients) {
+    if (client_id) {
+      entry = silc_idlist_find_client_by_id(server->global_list, 
+                                           client_id, NULL);
+      if (entry) {
+       clients = silc_calloc(1, sizeof(*clients));
+       clients[0] = entry;
+       clients_count = 1;
+      }
+    } else {
+      clients = silc_idlist_get_clients_by_nickname(server->global_list, 
+                                                   nick, server_name,
+                                                   &clients_count);
+    }
+  }
+  
+  if (!clients) {
+    /* Such a client really does not exist in the SILC network. */
     silc_server_command_send_status_data(cmd, SILC_COMMAND_IDENTIFY,
                                         SILC_STATUS_ERR_NO_SUCH_NICK,
-                                        3, tmp, strlen(tmp));
+                                        3, nick, strlen(nick));
     goto out;
   }
 
-  /* Send IDENTIFY reply */
-  idp = silc_id_payload_encode(entry->id, SILC_ID_CLIENT);
-  tmp = silc_argument_get_first_arg(cmd->args, NULL);
-  packet = silc_command_reply_payload_encode_va(SILC_COMMAND_IDENTIFY,
-                                               SILC_STATUS_OK, 0, 2,
-                                               2, idp->data, idp->len, 
-                                               3, nick, strlen(nick));
-  if (cmd->packet->flags & SILC_PACKET_FLAG_FORWARDED) {
-    void *id = silc_id_str2id(cmd->packet->src_id, cmd->packet->src_id_type);
-    silc_server_packet_send_dest(server, cmd->sock, 
-                                SILC_PACKET_COMMAND_REPLY, 0,
-                                id, cmd->packet->src_id_type,
-                                packet->data, packet->len, FALSE);
-    silc_free(id);
-  } else {
-    silc_server_packet_send(server, cmd->sock, 
-                           SILC_PACKET_COMMAND_REPLY, 0, 
-                           packet->data, packet->len, FALSE);
+  /* Check that all mandatory fields are present and request those data
+     from the server who owns the client if necessary. */
+  if (!cmd->pending && server->server_type == SILC_ROUTER &&
+      !silc_server_command_identify_check(cmd, clients, clients_count)) {
+    ret = -1;
+    goto out;
   }
 
-  silc_buffer_free(packet);
-  silc_buffer_free(idp);
+  /* Send the command reply to the client */
+  silc_server_command_identify_send_reply(cmd, clients, clients_count);
+
+ out:
+  if (clients)
+    silc_free(clients);
   if (client_id)
     silc_free(client_id);
+  if (nick)
+    silc_free(nick);
+  if (server_name)
+    silc_free(server_name);
+
+  return ret;
+}
+
+static int
+silc_server_command_identify_from_server(SilcServerCommandContext cmd)
+{
+  SilcServer server = cmd->server;
+  char *nick = NULL, *server_name = NULL;
+  int count = 0, clients_count;
+  SilcClientID *client_id = NULL;
+  SilcClientEntry *clients = NULL, entry;
+  int ret = 0;
+
+  /* Parse the IDENTIFY request */
+  if (!silc_server_command_identify_parse(cmd, &client_id, &nick, 
+                                         &server_name, &count))
+    return 0;
+
+  /* Process the command request. Let's search for the requested client and
+     send reply to the requesting server. */
+
+  if (client_id) {
+    entry = silc_idlist_find_client_by_id(server->local_list, client_id, NULL);
+    if (entry) {
+      clients = silc_calloc(1, sizeof(*clients));
+      clients[0] = entry;
+      clients_count = 1;
+    }
+  } else {
+    clients = silc_idlist_get_clients_by_nickname(server->local_list, 
+                                                 nick, server_name,
+                                                 &clients_count);
+    if (!clients)
+      clients = silc_idlist_get_clients_by_hash(server->local_list, 
+                                               nick, server->md5hash,
+                                               &clients_count);
+  }
+  
+  /* If we are router we will check our global list as well. */
+  if (!clients && server->server_type == SILC_ROUTER) {
+    if (client_id) {
+      entry = silc_idlist_find_client_by_id(server->global_list, 
+                                           client_id, NULL);
+      if (entry) {
+       clients = silc_calloc(1, sizeof(*clients));
+       clients[0] = entry;
+       clients_count = 1;
+      }
+    } else {
+      clients = silc_idlist_get_clients_by_nickname(server->global_list, 
+                                                   nick, server_name,
+                                                   &clients_count);
+      if (!clients)
+       clients = silc_idlist_get_clients_by_hash(server->global_list, 
+                                                 nick, server->md5hash,
+                                                 &clients_count);
+    }
+  }
+
+  if (!clients) {
+    /* Such a client really does not exist in the SILC network. */
+    silc_server_command_send_status_data(cmd, SILC_COMMAND_IDENTIFY,
+                                        SILC_STATUS_ERR_NO_SUCH_NICK,
+                                        3, nick, strlen(nick));
+    goto out;
+  }
+
+  /* Check that all mandatory fields are present and request those data
+     from the server who owns the client if necessary. */
+  if (!cmd->pending && server->server_type == SILC_ROUTER &&
+      !silc_server_command_identify_check(cmd, clients, clients_count)) {
+    ret = -1;
+    goto out;
+  }
+
+  /* Send the command reply */
+  silc_server_command_identify_send_reply(cmd, clients, clients_count);
 
  out:
+  if (clients)
+    silc_free(clients);
+  if (client_id)
+    silc_free(client_id);
   if (nick)
     silc_free(nick);
   if (server_name)
     silc_free(server_name);
-  silc_server_command_free(cmd);
+
+  return ret;
+}
+
+SILC_SERVER_CMD_FUNC(identify)
+{
+  SilcServerCommandContext cmd = (SilcServerCommandContext)context;
+  int ret;
+
+  SILC_SERVER_COMMAND_CHECK_ARGC(SILC_COMMAND_IDENTIFY, cmd, 1, 3);
+
+  if (cmd->sock->type == SILC_SOCKET_TYPE_CLIENT)
+    ret = silc_server_command_identify_from_client(cmd);
+  else
+    ret = silc_server_command_identify_from_server(cmd);
+
+  if (!ret)
+    silc_server_command_free(cmd);
 }
 
 /* Checks string for bad characters and returns TRUE if they are found. */
@@ -669,14 +1162,7 @@ SILC_SERVER_CMD_FUNC(nick)
   SilcClientID *new_id;
   char *nick;
 
-  SILC_LOG_DEBUG(("Start"));
-
-  /* Check number of arguments */
-  if (silc_argument_get_arg_num(cmd->args) < 1) {
-    silc_server_command_send_status_reply(cmd, SILC_COMMAND_NICK,
-                                         SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
-    goto out;
-  }
+  SILC_SERVER_COMMAND_CHECK_ARGC(SILC_COMMAND_NICK, cmd, 1, 1);
 
   /* Check nickname */
   nick = silc_argument_get_arg_type(cmd->args, 1, NULL);
@@ -689,21 +1175,15 @@ SILC_SERVER_CMD_FUNC(nick)
   /* Create new Client ID */
   silc_id_create_client_id(cmd->server->id, cmd->server->rng, 
                           cmd->server->md5hash, nick,
-                          &new_id);
-
-  /* Send notify about nickname change to our router. We send the new
-     ID and ask to replace it with the old one. */
-  if (cmd->server->server_type == SILC_SERVER && !cmd->server->standalone)
-    silc_server_send_replace_id(server, server->id_entry->router->connection, 
-                               FALSE, client->id,
-                               SILC_ID_CLIENT, SILC_ID_CLIENT_LEN,
-                               new_id, SILC_ID_CLIENT, SILC_ID_CLIENT_LEN);
+                          &new_id);
 
-  /* If we are router we have to distribute the new Client ID to all 
-     routers in SILC. */
-  if (cmd->server->server_type == SILC_ROUTER && !cmd->server->standalone)
-    silc_server_send_replace_id(server, server->id_entry->router->connection,  
-                               TRUE, client->id,
+  /* 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);
 
@@ -772,18 +1252,9 @@ SILC_SERVER_CMD_FUNC(topic)
   unsigned char *tmp;
   unsigned int argc, tmp_len;
 
-  /* Check number of arguments */
+  SILC_SERVER_COMMAND_CHECK_ARGC(SILC_COMMAND_TOPIC, cmd, 1, 2);
+
   argc = silc_argument_get_arg_num(cmd->args);
-  if (argc < 1) {
-    silc_server_command_send_status_reply(cmd, SILC_COMMAND_TOPIC,
-                                         SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
-    goto out;
-  }
-  if (argc > 2) {
-    silc_server_command_send_status_reply(cmd, SILC_COMMAND_TOPIC,
-                                         SILC_STATUS_ERR_TOO_MANY_PARAMS);
-    goto out;
-  }
 
   /* Get Channel ID */
   tmp = silc_argument_get_arg_type(cmd->args, 1, &tmp_len);
@@ -795,7 +1266,8 @@ SILC_SERVER_CMD_FUNC(topic)
   channel_id = silc_id_payload_parse_id(tmp, tmp_len);
 
   /* Check whether the channel exists */
-  channel = silc_idlist_find_channel_by_id(server->local_list, channel_id);
+  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);
@@ -839,7 +1311,7 @@ SILC_SERVER_CMD_FUNC(topic)
     idp = silc_id_payload_encode(client->id, SILC_ID_CLIENT);
 
     /* Send notify about topic change to all clients on the channel */
-    silc_server_send_notify_to_channel(server, channel,
+    silc_server_send_notify_to_channel(server, channel, TRUE,
                                       SILC_NOTIFY_TYPE_TOPIC_SET, 2,
                                       idp->data, idp->len,
                                       channel->topic, strlen(channel->topic));
@@ -882,20 +1354,9 @@ SILC_SERVER_CMD_FUNC(invite)
   SilcChannelID *channel_id;
   SilcBuffer sidp;
   unsigned char *tmp;
-  unsigned int argc, len;
+  unsigned int len;
 
-  /* Check number of arguments */
-  argc = silc_argument_get_arg_num(cmd->args);
-  if (argc < 1) {
-    silc_server_command_send_status_reply(cmd, SILC_COMMAND_INVITE,
-                                         SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
-    goto out;
-  }
-  if (argc > 2) {
-    silc_server_command_send_status_reply(cmd, SILC_COMMAND_INVITE,
-                                         SILC_STATUS_ERR_TOO_MANY_PARAMS);
-    goto out;
-  }
+  SILC_SERVER_COMMAND_CHECK_ARGC(SILC_COMMAND_INVITE, cmd, 1, 2);
 
   /* Get destination ID */
   tmp = silc_argument_get_arg_type(cmd->args, 1, &len);
@@ -916,7 +1377,8 @@ SILC_SERVER_CMD_FUNC(invite)
   channel_id = silc_id_payload_parse_id(tmp, len);
 
   /* Check whether the channel exists */
-  channel = silc_idlist_find_channel_by_id(server->local_list, channel_id);
+  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);
@@ -941,7 +1403,7 @@ SILC_SERVER_CMD_FUNC(invite)
       if (chl->client == sender) {
        if (chl->mode == SILC_CHANNEL_UMODE_NONE) {
          silc_server_command_send_status_reply(cmd, SILC_COMMAND_INVITE,
-                                               SILC_STATUS_ERR_NO_CHANNEL_PRIV);
+                                       SILC_STATUS_ERR_NO_CHANNEL_PRIV);
          goto out;
        }
        break;
@@ -950,11 +1412,11 @@ SILC_SERVER_CMD_FUNC(invite)
 
   /* Find the connection data for the destination. If it is local we will
      send it directly otherwise we will send it to router for routing. */
-  dest = silc_idlist_find_client_by_id(server->local_list, dest_id);
+  dest = silc_idlist_find_client_by_id(server->local_list, dest_id, NULL);
   if (dest)
     dest_sock = (SilcSocketConnection)dest->connection;
   else
-    dest_sock = silc_server_get_route(server, dest_id, SILC_ID_CLIENT);
+    dest_sock = silc_server_route_get(server, dest_id, SILC_ID_CLIENT);
 
   /* Check whether the requested client is already on the channel. */
   /* XXX if we are normal server we don't know about global clients on
@@ -1030,20 +1492,9 @@ SILC_SERVER_CMD_FUNC(info)
   SilcServerCommandContext cmd = (SilcServerCommandContext)context;
   SilcServer server = cmd->server;
   SilcBuffer packet, idp;
-  unsigned int argc;
   char info_string[256], *dest_server;
 
-  argc = silc_argument_get_arg_num(cmd->args);
-  if (argc < 1) {
-    silc_server_command_send_status_reply(cmd, SILC_COMMAND_INFO,
-                                         SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
-    goto out;
-  }
-  if (argc > 1) {
-    silc_server_command_send_status_reply(cmd, SILC_COMMAND_INFO,
-                                         SILC_STATUS_ERR_TOO_MANY_PARAMS);
-    goto out;
-  }
+  SILC_SERVER_COMMAND_CHECK_ARGC(SILC_COMMAND_INFO, cmd, 1, 1);
 
   /* Get server name */
   dest_server = silc_argument_get_arg_type(cmd->args, 1, NULL);
@@ -1102,20 +1553,10 @@ SILC_SERVER_CMD_FUNC(ping)
   SilcServerCommandContext cmd = (SilcServerCommandContext)context;
   SilcServer server = cmd->server;
   SilcServerID *id;
-  unsigned int argc, len;
+  unsigned int len;
   unsigned char *tmp;
 
-  argc = silc_argument_get_arg_num(cmd->args);
-  if (argc < 1) {
-    silc_server_command_send_status_reply(cmd, SILC_COMMAND_PING,
-                                         SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
-    goto out;
-  }
-  if (argc > 2) {
-    silc_server_command_send_status_reply(cmd, SILC_COMMAND_PING,
-                                         SILC_STATUS_ERR_TOO_MANY_PARAMS);
-    goto out;
-  }
+  SILC_SERVER_COMMAND_CHECK_ARGC(SILC_COMMAND_INFO, cmd, 1, 2);
 
   /* Get Server ID */
   tmp = silc_argument_get_arg_type(cmd->args, 1, &len);
@@ -1124,7 +1565,9 @@ SILC_SERVER_CMD_FUNC(ping)
                                          SILC_STATUS_ERR_NO_SERVER_ID);
     goto out;
   }
-  id = silc_id_payload_parse_id(tmp, len);
+  id = silc_id_str2id(tmp, SILC_ID_SERVER);
+  if (!id)
+    goto out;
 
   if (!SILC_ID_SERVER_COMPARE(id, server->id)) {
     /* Send our reply */
@@ -1165,16 +1608,29 @@ SILC_TASK_CALLBACK(silc_server_command_join_notify)
 
     clidp = silc_id_payload_encode(ctx->client->id, SILC_ID_CLIENT);
 
-    silc_server_send_notify_to_channel(ctx->server, ctx->channel,
+    silc_server_send_notify_to_channel(ctx->server, ctx->channel, FALSE,
                                       SILC_NOTIFY_TYPE_JOIN, 1,
                                       clidp->data, clidp->len);
+#if 0
+    /* Send NEW_CHANNEL_USER packet to primary route */
+    silc_server_send_new_channel_user(server, server->router->connection,
+                                     server->server_type == SILC_SERVER ?
+                                     FALSE : TRUE,
+                                     channel->id, SILC_ID_CHANNEL_LEN,
+                                     client->id, SILC_ID_CLIENT_LEN);
+#endif
+
+    /* Send NAMES command reply to the joined channel so the user sees who
+       is currently on the channel. */
+    silc_server_command_send_names(ctx->server, ctx->client->connection, 
+                                  ctx->channel);
 
     silc_buffer_free(clidp);
     silc_free(ctx);
   } else {
     silc_task_register(ctx->server->timeout_queue, fd,
                       silc_server_command_join_notify, context,
-                      0, 300000, SILC_TASK_TIMEOUT, SILC_TASK_PRI_LOW);
+                      0, 200000, SILC_TASK_TIMEOUT, SILC_TASK_PRI_LOW);
   }
 }
 
@@ -1205,55 +1661,66 @@ void silc_server_command_send_names(SilcServer server,
   silc_free(idp);
 }
 
-/* Server side of command JOIN. Joins client into requested channel. If 
-   the channel does not exist it will be created. */
+/* Internal routine that is called after router has replied to server's
+   JOIN command it forwarded to the router. The route has joined and possibly
+   creaetd the channel. This function adds the client to the channel's user
+   list. */
 
-SILC_SERVER_CMD_FUNC(join)
+SILC_SERVER_CMD_FUNC(add_to_channel)
 {
   SilcServerCommandContext cmd = (SilcServerCommandContext)context;
   SilcServer server = cmd->server;
-  SilcSocketConnection sock = cmd->sock;
-  SilcBuffer buffer = cmd->packet->buffer;
-  int argc, i, k, tmp_len;
-  char *tmp, *channel_name = NULL, *cipher = NULL;
-  unsigned char *passphrase = NULL, mode[4];
-  unsigned int umode = 0;
+  SilcClientEntry client;
   SilcChannelEntry channel;
   SilcChannelClientEntry chl;
-  SilcServerID *router_id;
-  SilcBuffer packet, idp;
-  SilcClientEntry client;
+  char *channel_name;
 
-  SILC_LOG_DEBUG(("Start"));
+  /* Get channel name */
+  channel_name = silc_argument_get_arg_type(cmd->args, 1, NULL);
 
-  /* Check number of parameters */
-  argc = silc_argument_get_arg_num(cmd->args);
-  if (argc < 1) {
-    silc_server_command_send_status_reply(cmd, SILC_COMMAND_JOIN,
-                                         SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
-    goto out;
-  }
-  if (argc > 3) {
-    silc_server_command_send_status_reply(cmd, SILC_COMMAND_JOIN,
-                                         SILC_STATUS_ERR_TOO_MANY_PARAMS);
-    goto out;
-  }
+  /* Get client entry */
+  client = (SilcClientEntry)cmd->sock->user_data;
 
-  /* Get channel name */
-  tmp = silc_argument_get_arg_type(cmd->args, 1, &tmp_len);
-  if (!tmp) {
-    silc_server_command_send_status_reply(cmd, SILC_COMMAND_JOIN,
-                                         SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
-    goto out;
+  /* Get channel entry */
+  channel = silc_idlist_find_channel_by_name(server->local_list, 
+                                            channel_name, NULL);
+  if (channel) {
+    /* Join the client to the channel by adding it to channel's user list.
+       Add also the channel to client entry's channels list for fast cross-
+       referencing. */
+    chl = silc_calloc(1, sizeof(*chl));
+    //chl->mode = SILC_CHANNEL_UMODE_CHANOP | SILC_CHANNEL_UMODE_CHANFO;
+    chl->client = client;
+    chl->channel = channel;
+    silc_list_add(channel->user_list, chl);
+    silc_list_add(client->channels, chl);
   }
-  channel_name = tmp;
 
-  if (silc_server_command_bad_chars(channel_name) == TRUE) {
-    silc_server_command_send_status_reply(cmd, SILC_COMMAND_JOIN,
-                                         SILC_STATUS_ERR_BAD_CHANNEL);
-    silc_free(channel_name);
-    goto out;
-  }
+  silc_server_command_free(cmd);
+}
+
+/* Internal routine to join channel. The channel sent to this function
+   has been either created or resolved from ID lists. This joins the sent
+   client to the channel. */
+
+static void silc_server_command_join_channel(SilcServer server, 
+                                            SilcServerCommandContext cmd,
+                                            SilcChannelEntry channel,
+                                            int created,
+                                            unsigned int umode)
+{
+  SilcSocketConnection sock = cmd->sock;
+  unsigned char *tmp;
+  unsigned int tmp_len;
+  unsigned char *passphrase = NULL, mode[4];
+  SilcClientEntry client;
+  SilcChannelClientEntry chl;
+  SilcBuffer reply, chidp, clidp;
+
+  SILC_LOG_DEBUG(("Start"));
+
+  if (!channel)
+    return;
 
   /* Get passphrase */
   tmp = silc_argument_get_arg_type(cmd->args, 2, &tmp_len);
@@ -1262,67 +1729,6 @@ SILC_SERVER_CMD_FUNC(join)
     memcpy(passphrase, tmp, tmp_len);
   }
   
-  /* Get cipher name */
-  cipher = silc_argument_get_arg_type(cmd->args, 3, NULL);
-
-  /* See if the channel exists */
-  channel = 
-    silc_idlist_find_channel_by_name(server->local_list, channel_name);
-  if (!channel) {
-    /* Channel not found */
-
-    /* If we are standalone server we don't have a router, we just create 
-       the channel by  ourselves. */
-    if (server->standalone) {
-      router_id = server->id;
-      channel = silc_server_new_channel(server, router_id, cipher, 
-                                       channel_name);
-      umode |= SILC_CHANNEL_UMODE_CHANOP;
-      umode |= SILC_CHANNEL_UMODE_CHANFO;
-      if (!channel)
-       goto out;
-
-      goto join_channel;
-    }
-
-    /* No channel ID found, the channel does not exist on our server.
-       We send JOIN command to our router which will handle the joining
-       procedure (either creates the channel if it doesn't exist or
-       joins the client to it) - if we are normal server. */
-    if (server->server_type == SILC_SERVER) {
-
-      /* Forward the original JOIN command to the router */
-      silc_buffer_push(buffer, buffer->data - buffer->head);
-      silc_server_packet_forward(server, (SilcSocketConnection)
-                                server->id_entry->router->connection,
-                                buffer->data, buffer->len, TRUE);
-      
-      /* Add the command to be pending. It will be re-executed after
-        router has replied back to us. */
-      cmd->pending = TRUE;
-      silc_server_command_pending(SILC_COMMAND_JOIN, 0,
-                                 silc_server_command_join, context);
-      return;
-    }
-  }
-
-  /* If we are router and the channel does not exist we will check our
-     global list for the channel. */
-  if (!channel && server->server_type == SILC_ROUTER) {
-
-    /* Notify all routers about the new channel in SILC network. */
-    if (!server->standalone) {
-#if 0
-      silc_server_send_new_id(server, server->id_entry->router->connection, 
-                             TRUE,
-                             xxx, SILC_ID_CHANNEL, SILC_ID_CHANNEL_LEN);
-#endif
-    }
-
-  }
-
- join_channel:
-
   /*
    * Check channel modes
    */
@@ -1370,16 +1776,18 @@ SILC_SERVER_CMD_FUNC(join)
   /* If the JOIN request was forwarded to us we will make a bit slower
      query to get the client pointer. Otherwise, we get the client pointer
      real easy. */
-  if (!(cmd->packet->flags & SILC_PACKET_FLAG_FORWARDED)) {
-    client = (SilcClientEntry)sock->user_data;
-  } else {
+  if (cmd->packet->flags & SILC_PACKET_FLAG_FORWARDED) {
     void *id = silc_id_str2id(cmd->packet->src_id, cmd->packet->src_id_type);
-    client = silc_idlist_find_client_by_id(server->local_list, id);
+    client = silc_idlist_find_client_by_id(server->local_list, id, NULL);
     if (!client) {
       /* XXX */
+      SILC_LOG_ERROR(("Forwarded join command did not find the client who "
+                     "wanted to join the channel"));
       goto out;
     }
     silc_free(id);
+  } else {
+    client = (SilcClientEntry)sock->user_data;
   }
 
   /* Check whether the client already is on the channel */
@@ -1389,6 +1797,10 @@ SILC_SERVER_CMD_FUNC(join)
     goto out;
   }
 
+  /* Generate new channel key as protocol dictates */
+  if (!created)
+    silc_server_create_channel_key(server, channel, 0);
+
   /* Join the client to the channel by adding it to channel's user list.
      Add also the channel to client entry's channels list for fast cross-
      referencing. */
@@ -1399,100 +1811,188 @@ SILC_SERVER_CMD_FUNC(join)
   silc_list_add(channel->user_list, chl);
   silc_list_add(client->channels, chl);
 
-  /* Notify router about new user on channel. If we are normal server
-     we send it to our router, if we are router we send it to our
-     primary route. */
-  if (!server->standalone) {
-
+  /* Encode Client ID Payload of the original client who wants to join */
+  clidp = silc_id_payload_encode(client->id, SILC_ID_CLIENT);
+
+  /* Encode command reply packet */
+  chidp = silc_id_payload_encode(channel->id, SILC_ID_CHANNEL);
+  SILC_PUT32_MSB(channel->mode, mode);
+  if (!channel->topic) {
+    reply = 
+      silc_command_reply_payload_encode_va(SILC_COMMAND_JOIN,
+                                          SILC_STATUS_OK, 0, 3,
+                                          2, channel->channel_name,
+                                          strlen(channel->channel_name),
+                                          3, chidp->data, chidp->len,
+                                          4, mode, 4);
+  } else {
+    reply = 
+      silc_command_reply_payload_encode_va(SILC_COMMAND_JOIN,
+                                          SILC_STATUS_OK, 0, 4, 
+                                          2, channel->channel_name, 
+                                          strlen(channel->channel_name),
+                                          3, chidp->data, chidp->len,
+                                          4, mode, 4,
+                                          5, channel->topic, 
+                                          strlen(channel->topic));
   }
+    
+  if (server->server_type == SILC_ROUTER && 
+      cmd->packet->flags & SILC_PACKET_FLAG_FORWARDED) {
+    /* We are router and server has forwarded this command to us. Send
+       all replys to the server. */
+    void *tmpid;
+
+    /* Send command reply destined to the original client */
+    tmpid = silc_id_str2id(cmd->packet->src_id, cmd->packet->src_id_type);
+    silc_server_packet_send_dest(cmd->server, sock, 
+                                SILC_PACKET_COMMAND_REPLY, 0,
+                                tmpid, cmd->packet->src_id_type,
+                                reply->data, reply->len, FALSE);
+
+    /* Distribute new channel key to local cell and local clients. */
+    silc_server_send_channel_key(server, channel, FALSE);
+    
+    /* Distribute JOIN notify into the cell for everbody on the channel */
+    silc_server_send_notify_to_channel(server, channel, FALSE,
+                                      SILC_NOTIFY_TYPE_JOIN, 1,
+                                      clidp->data, clidp->len);
 
-  /* Send command reply to the client. Client receives the Channe ID,
-     channel mode and possibly other information in this reply packet. */
-  if (!cmd->pending) {
-    idp = silc_id_payload_encode(channel->id, SILC_ID_CHANNEL);
-    SILC_PUT32_MSB(channel->mode, mode);
+    /* Broadcast NEW_CHANNEL_USER packet to primary route */
+    silc_server_send_new_channel_user(server, server->router->connection,
+                                     TRUE, channel->id, SILC_ID_CHANNEL_LEN,
+                                     client->id, SILC_ID_CLIENT_LEN);
 
-    if (!channel->topic)
-      packet = 
-       silc_command_reply_payload_encode_va(SILC_COMMAND_JOIN,
-                                            SILC_STATUS_OK, 0, 3,
-                                            2, channel_name, 
-                                            strlen(channel_name),
-                                            3, idp->data, idp->len,
-                                            4, mode, 4);
-    else
-      packet = 
-       silc_command_reply_payload_encode_va(SILC_COMMAND_JOIN,
-                                            SILC_STATUS_OK, 0, 4, 
-                                            2, channel_name, 
-                                            strlen(channel_name),
-                                            3, idp->data, idp->len,
-                                            4, mode, 4,
-                                            5, channel->topic, 
-                                            strlen(channel->topic));
-
-    if (cmd->packet->flags & SILC_PACKET_FLAG_FORWARDED) {
-      void *id = silc_id_str2id(cmd->packet->src_id, cmd->packet->src_id_type);
-      silc_server_packet_send_dest(cmd->server, cmd->sock, 
-                                  SILC_PACKET_COMMAND_REPLY, 0,
-                                  id, cmd->packet->src_id_type,
-                                  packet->data, packet->len, FALSE);
-      silc_free(id);
-    } else
-      silc_server_packet_send(server, sock, SILC_PACKET_COMMAND_REPLY, 0, 
-                             packet->data, packet->len, FALSE);
-    silc_buffer_free(packet);
+    silc_free(tmpid);
+  } else {
+    /* Client sent the command. Send all replies directly to the client. */
 
-    /* Send channel key to the client. Client cannot start transmitting
-       to the channel until we have sent the key. */
-    tmp_len = strlen(channel->channel_key->cipher->name);
-    packet = 
-      silc_channel_key_payload_encode(idp->len, idp->data, 
-                                     strlen(channel->channel_key->
-                                            cipher->name),
-                                     channel->channel_key->cipher->name,
-                                     channel->key_len / 8, channel->key);
+    /* Send command reply */
+    silc_server_packet_send(server, sock, SILC_PACKET_COMMAND_REPLY, 0, 
+                           reply->data, reply->len, FALSE);
+
+    /* Send the channel key. Channel key is sent before any other packet
+       to the channel. */
+    silc_server_send_channel_key(server, channel, server->standalone ?
+                                FALSE : TRUE);
     
-    silc_server_packet_send(server, sock, SILC_PACKET_CHANNEL_KEY, 0, 
-                           packet->data, packet->len, FALSE);
+    /* 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_buffer_free(packet);
-    silc_buffer_free(idp);
+    /* Send NEW_CHANNEL_USER packet to our primary router */
+    if (!server->standalone)
+      silc_server_send_new_channel_user(server, server->router->connection,
+                                       FALSE, 
+                                       channel->id, SILC_ID_CHANNEL_LEN,
+                                       client->id, SILC_ID_CLIENT_LEN);
+
+    /* Send NAMES command reply to the joined channel so the user sees who
+       is currently on the channel. */
+    silc_server_command_send_names(server, sock, channel);
   }
 
-  /* Finally, send notify message to all clients on the channel about
-     new user on the channel. */
-  if (!(cmd->packet->flags & SILC_PACKET_FLAG_FORWARDED)) {
-    if (!cmd->pending) {
-      SilcBuffer clidp;
+  silc_buffer_free(reply);
+  silc_buffer_free(clidp);
+  silc_buffer_free(chidp);
 
-      clidp = silc_id_payload_encode(client->id, SILC_ID_CLIENT);
-      
-      silc_server_send_notify_to_channel(server, channel,
-                                        SILC_NOTIFY_TYPE_JOIN, 1,
-                                        clidp->data, clidp->len);
-      
-      silc_buffer_free(clidp);
+ out:
+  if (passphrase)
+    silc_free(passphrase);
+}
+
+/* Server side of command JOIN. Joins client into requested channel. If 
+   the channel does not exist it will be created. */
+
+SILC_SERVER_CMD_FUNC(join)
+{
+  SilcServerCommandContext cmd = (SilcServerCommandContext)context;
+  SilcServer server = cmd->server;
+  int tmp_len;
+  char *tmp, *channel_name = NULL, *cipher = NULL;
+  SilcChannelEntry channel;
+  unsigned int umode = 0;
+  int created = FALSE;
+
+  SILC_SERVER_COMMAND_CHECK_ARGC(SILC_COMMAND_JOIN, cmd, 1, 3);
+
+  /* Get channel name */
+  tmp = silc_argument_get_arg_type(cmd->args, 1, &tmp_len);
+  if (!tmp) {
+    silc_server_command_send_status_reply(cmd, SILC_COMMAND_JOIN,
+                                         SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
+    goto out;
+  }
+  channel_name = tmp;
+
+  if (silc_server_command_bad_chars(channel_name) == TRUE) {
+    silc_server_command_send_status_reply(cmd, SILC_COMMAND_JOIN,
+                                         SILC_STATUS_ERR_BAD_CHANNEL);
+    silc_free(channel_name);
+    goto out;
+  }
+
+  /* Get cipher name */
+  cipher = silc_argument_get_arg_type(cmd->args, 3, NULL);
+
+  /* See if the channel exists */
+  channel = silc_idlist_find_channel_by_name(server->local_list, 
+                                            channel_name, NULL);
+  if (!channel) {
+    /* Channel not found */
+
+    /* If we are standalone server we don't have a router, we just create 
+       the channel by ourselves. */
+    if (server->standalone) {
+      channel = silc_server_create_new_channel(server, server->id, cipher, 
+                                              channel_name);
+      umode |= SILC_CHANNEL_UMODE_CHANOP;
+      umode |= SILC_CHANNEL_UMODE_CHANFO;
+      created = TRUE;
     } else {
-      /* This is pending command request. Send the notify after we have
-        received the key for the channel from the router. */
-      JoinInternalContext *ctx = silc_calloc(1, sizeof(*ctx));
-      ctx->channel_name = channel_name;
-      ctx->nickname = client->nickname;
-      ctx->username = client->username;
-      ctx->hostname = sock->hostname ? sock->hostname : sock->ip;
-      ctx->channel = channel;
-      ctx->server = server;
-      ctx->client = client;
-      silc_task_register(server->timeout_queue, sock->sock,
-                        silc_server_command_join_notify, ctx,
-                        0, 10000, SILC_TASK_TIMEOUT, SILC_TASK_PRI_LOW);
+
+      /* The channel does not exist on our server. If we are normal server 
+        we will send JOIN command to our router which will handle the joining
+        procedure (either creates the channel if it doesn't exist or joins
+        the client to it). */
+      if (server->server_type == SILC_SERVER) {
+       /* Forward the original JOIN command to the router */
+       silc_buffer_push(cmd->packet->buffer, 
+                        cmd->packet->buffer->data - 
+                        cmd->packet->buffer->head);
+       silc_server_packet_forward(server, (SilcSocketConnection)
+                                  server->router->connection,
+                                  cmd->packet->buffer->data, 
+                                  cmd->packet->buffer->len, TRUE);
+
+       /* Register handler that will be called after the router has replied
+          to us. We will add the client to the new channel in this callback
+          function. Will be called from JOIN command reply. */
+       silc_server_command_pending(server, SILC_COMMAND_JOIN, 
+                                   ++server->router->data.cmd_ident,
+                                   silc_server_command_add_to_channel,
+                                   context);
+       return;
+      }
+      
+      /* We are router and the channel does not seem exist so we will check
+        our global list as well for the channel. */
+      channel = silc_idlist_find_channel_by_name(server->global_list, 
+                                                channel_name, NULL);
+      if (!channel) {
+       /* Channel really does not exist, create it */
+       channel = silc_server_create_new_channel(server, server->id, cipher, 
+                                                channel_name);
+       umode |= SILC_CHANNEL_UMODE_CHANOP;
+       umode |= SILC_CHANNEL_UMODE_CHANFO;
+       created = TRUE;
+      }
     }
   }
 
-  /* Send NAMES command reply to the joined channel so the user sees who
-     is currently on the channel. */
-  silc_server_command_send_names(server, sock, channel);
+  /* Join to the channel */
+  silc_server_command_join_channel(server, cmd, channel, created, umode);
 
  out:
   silc_server_command_free(cmd);
@@ -1505,23 +2005,10 @@ SILC_SERVER_CMD_FUNC(motd)
 {
   SilcServerCommandContext cmd = (SilcServerCommandContext)context;
   SilcServer server = cmd->server;
-  unsigned int argc;
   char *motd;
   int motd_len;
   
-  SILC_LOG_DEBUG(("Start"));
-
-  argc = silc_argument_get_arg_num(cmd->args);
-  if (argc < 1) {
-    silc_server_command_send_status_reply(cmd, SILC_COMMAND_LEAVE,
-                                         SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
-    goto out;
-  }
-  if (argc > 2) {
-    silc_server_command_send_status_reply(cmd, SILC_COMMAND_LEAVE,
-                                         SILC_STATUS_ERR_TOO_MANY_PARAMS);
-    goto out;
-  }
+  SILC_SERVER_COMMAND_CHECK_ARGC(SILC_COMMAND_MOTD, cmd, 1, 2);
 
   /* XXX show currently only our motd */
 
@@ -1578,7 +2065,7 @@ int silc_server_check_cmode_rights(SilcChannelEntry channel,
     if (is_op && !is_fo)
       return FALSE;
   } else {
-    if (channel->mode & SILC_CHANNEL_MODE_PRIVATE) {
+    if (channel->mode & SILC_CHANNEL_MODE_PRIVKEY) {
       if (is_op && !is_fo)
        return FALSE;
     }
@@ -1613,16 +2100,13 @@ SILC_SERVER_CMD_FUNC(cmode)
 {
   SilcServerCommandContext cmd = (SilcServerCommandContext)context;
   SilcServer server = cmd->server;
-  SilcSocketConnection sock = cmd->sock;
   SilcClientEntry client = (SilcClientEntry)cmd->sock->user_data;
   SilcChannelID *channel_id;
-  SilcClientID *client_id;
   SilcChannelEntry channel;
   SilcChannelClientEntry chl;
   SilcBuffer packet, cidp;
   unsigned char *tmp, *tmp_id, *tmp_mask;
   unsigned int argc, mode_mask, tmp_len, tmp_len2;
-  int i;
 
   SILC_LOG_DEBUG(("Start"));
 
@@ -1657,7 +2141,8 @@ SILC_SERVER_CMD_FUNC(cmode)
   SILC_GET32_MSB(mode_mask, tmp_mask);
 
   /* Get channel entry */
-  channel = silc_idlist_find_channel_by_id(server->local_list, channel_id);
+  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);
@@ -1698,23 +2183,12 @@ SILC_SERVER_CMD_FUNC(cmode)
       /* The mode is removed and we need to generate and distribute
         new channel key. Clients are not using private channel keys
         anymore after this. */
-      unsigned int key_len;
-      unsigned char channel_key[32];
 
       /* XXX Duplicated code, make own function for this!! LEAVE uses this
         as well */
 
       /* Re-generate channel key */
-      key_len = channel->key_len / 8;
-      for (i = 0; i < key_len; i++)
-       channel_key[i] = silc_rng_get_byte(server->rng);
-      channel->channel_key->cipher->set_key(channel->channel_key->context, 
-                                           channel_key, key_len);
-      memset(channel->key, 0, key_len);
-      silc_free(channel->key);
-      channel->key = silc_calloc(key_len, sizeof(*channel->key));
-      memcpy(channel->key, channel_key, key_len);
-      memset(channel_key, 0, sizeof(channel_key));
+      silc_server_create_channel_key(server, channel, 0);
       
       /* Encode channel key payload to be distributed on the channel */
       packet = 
@@ -1722,7 +2196,7 @@ SILC_SERVER_CMD_FUNC(cmode)
                                        strlen(channel->channel_key->
                                               cipher->name),
                                        channel->channel_key->cipher->name,
-                                       key_len, channel->key);
+                                       channel->key_len / 8, channel->key);
       
       /* If we are normal server then we will send it to our router.  If we
         are router we will send it to all local servers that has clients on
@@ -1730,7 +2204,7 @@ SILC_SERVER_CMD_FUNC(cmode)
       if (server->server_type == SILC_SERVER) {
        if (!server->standalone)
          silc_server_packet_send(server, 
-                                 cmd->server->id_entry->router->connection,
+                                 cmd->server->router->connection,
                                  SILC_PACKET_CHANNEL_KEY, 0, packet->data,
                                  packet->len, TRUE);
       } else {
@@ -1848,7 +2322,6 @@ SILC_SERVER_CMD_FUNC(cmode)
     if (!(channel->mode & SILC_CHANNEL_MODE_CIPHER)) {
       /* Cipher to use protect the traffic */
       unsigned int key_len = 128;
-      unsigned char channel_key[32];
       char *cp;
 
       /* Get cipher */
@@ -1871,20 +2344,12 @@ SILC_SERVER_CMD_FUNC(cmode)
       silc_cipher_free(channel->channel_key);
       silc_cipher_alloc(tmp, &channel->channel_key);
 
-      /* Re-generate channel key */
       key_len /= 8;
-      if (key_len > sizeof(channel_key))
-       key_len = sizeof(channel_key);
-
-      for (i = 0; i < key_len; i++)
-       channel_key[i] = silc_rng_get_byte(server->rng);
-      channel->channel_key->cipher->set_key(channel->channel_key->context, 
-                                           channel_key, key_len);
-      memset(channel->key, 0, key_len);
-      silc_free(channel->key);
-      channel->key = silc_calloc(key_len, sizeof(*channel->key));
-      memcpy(channel->key, channel_key, key_len);
-      memset(channel_key, 0, sizeof(channel_key));
+      if (key_len > 32)
+       key_len = 32;
+
+      /* Re-generate channel key */
+      silc_server_create_channel_key(server, channel, key_len);
     
       /* Encode channel key payload to be distributed on the channel */
       packet = 
@@ -1892,7 +2357,7 @@ SILC_SERVER_CMD_FUNC(cmode)
                                        strlen(channel->channel_key->
                                               cipher->name),
                                        channel->channel_key->cipher->name,
-                                       key_len, channel->key);
+                                       channel->key_len / 8, channel->key);
     
       /* If we are normal server then we will send it to our router.  If we
         are router we will send it to all local servers that has clients on
@@ -1900,7 +2365,7 @@ SILC_SERVER_CMD_FUNC(cmode)
       if (server->server_type == SILC_SERVER) {
        if (!server->standalone)
          silc_server_packet_send(server, 
-                                 cmd->server->id_entry->router->connection,
+                                 cmd->server->router->connection,
                                  SILC_PACKET_CHANNEL_KEY, 0, packet->data,
                                  packet->len, TRUE);
       } else {
@@ -1917,8 +2382,6 @@ SILC_SERVER_CMD_FUNC(cmode)
     if (channel->mode & SILC_CHANNEL_MODE_CIPHER) {
       /* Cipher mode is unset. Remove the cipher and revert back to 
         default cipher */
-      unsigned int key_len;
-      unsigned char channel_key[32];
 
       if (channel->mode_data.cipher) {
        silc_free(channel->mode_data.cipher);
@@ -1938,16 +2401,7 @@ SILC_SERVER_CMD_FUNC(cmode)
        silc_cipher_alloc(channel->cipher, &channel->channel_key);
 
       /* Re-generate channel key */
-      key_len = channel->key_len / 8;
-      for (i = 0; i < key_len; i++)
-       channel_key[i] = silc_rng_get_byte(server->rng);
-      channel->channel_key->cipher->set_key(channel->channel_key->context, 
-                                           channel_key, key_len);
-      memset(channel->key, 0, key_len);
-      silc_free(channel->key);
-      channel->key = silc_calloc(key_len, sizeof(*channel->key));
-      memcpy(channel->key, channel_key, key_len);
-      memset(channel_key, 0, sizeof(channel_key));
+      silc_server_create_channel_key(server, channel, 0);
       
       /* Encode channel key payload to be distributed on the channel */
       packet = 
@@ -1955,7 +2409,7 @@ SILC_SERVER_CMD_FUNC(cmode)
                                        strlen(channel->channel_key->
                                               cipher->name),
                                        channel->channel_key->cipher->name,
-                                       key_len, channel->key);
+                                       channel->key_len / 8, channel->key);
       
       /* If we are normal server then we will send it to our router.  If we
         are router we will send it to all local servers that has clients on
@@ -1963,7 +2417,7 @@ SILC_SERVER_CMD_FUNC(cmode)
       if (server->server_type == SILC_SERVER) {
        if (!server->standalone)
          silc_server_packet_send(server, 
-                                 cmd->server->id_entry->router->connection,
+                                 cmd->server->router->connection,
                                  SILC_PACKET_CHANNEL_KEY, 0, packet->data,
                                  packet->len, TRUE);
       } else {
@@ -1983,7 +2437,7 @@ 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, 
+  silc_server_send_notify_to_channel(server, channel, TRUE,
                                     SILC_NOTIFY_TYPE_CMODE_CHANGE, 2,
                                     cidp->data, cidp->len, 
                                     tmp_mask, tmp_len);
@@ -2009,7 +2463,6 @@ SILC_SERVER_CMD_FUNC(cumode)
 {
   SilcServerCommandContext cmd = (SilcServerCommandContext)context;
   SilcServer server = cmd->server;
-  SilcSocketConnection sock = cmd->sock;
   SilcClientEntry client = (SilcClientEntry)cmd->sock->user_data;
   SilcChannelID *channel_id;
   SilcClientID *client_id;
@@ -2017,23 +2470,11 @@ SILC_SERVER_CMD_FUNC(cumode)
   SilcClientEntry target_client;
   SilcChannelClientEntry chl;
   SilcBuffer packet, idp;
-  unsigned char *tmp, *tmp_id, *tmp_mask;
-  unsigned int argc, target_mask, sender_mask, tmp_len;
-  int i, notify = FALSE;
-
-  SILC_LOG_DEBUG(("Start"));
+  unsigned char *tmp_id, *tmp_mask;
+  unsigned int target_mask, sender_mask, tmp_len;
+  int notify = FALSE;
 
-  argc = silc_argument_get_arg_num(cmd->args);
-  if (argc < 3) {
-    silc_server_command_send_status_reply(cmd, SILC_COMMAND_CUMODE,
-                                         SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
-    goto out;
-  }
-  if (argc > 3) {
-    silc_server_command_send_status_reply(cmd, SILC_COMMAND_CUMODE,
-                                         SILC_STATUS_ERR_TOO_MANY_PARAMS);
-    goto out;
-  }
+  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);
@@ -2045,7 +2486,8 @@ SILC_SERVER_CMD_FUNC(cumode)
   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);
+  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);
@@ -2094,8 +2536,9 @@ SILC_SERVER_CMD_FUNC(cumode)
   client_id = silc_id_payload_parse_id(tmp_id, tmp_len);
 
   /* Get target client's entry */
-  target_client = silc_idlist_find_client_by_id(server->local_list, client_id);
-  if (!client) {
+  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 */
   }
 
@@ -2160,7 +2603,7 @@ SILC_SERVER_CMD_FUNC(cumode)
   /* 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,
+    silc_server_send_notify_to_channel(server, channel, TRUE,
                                       SILC_NOTIFY_TYPE_CUMODE_CHANGE, 3,
                                       idp->data, idp->len,
                                       tmp_mask, 4, tmp_id, tmp_len);
@@ -2187,11 +2630,6 @@ SILC_SERVER_CMD_FUNC(cumode)
 
 SILC_SERVER_CMD_FUNC(kick)
 {
-  SilcServerCommandContext cmd = (SilcServerCommandContext)context;
-  SilcServer server = cmd->server;
-  SilcSocketConnection sock = cmd->sock;
-  SilcClientEntry client = (SilcClientEntry)cmd->sock->user_data;
-
 }
 
 SILC_SERVER_CMD_FUNC(restart)
@@ -2221,22 +2659,10 @@ SILC_SERVER_CMD_FUNC(leave)
   SilcChannelID *id;
   SilcChannelEntry channel;
   SilcBuffer packet;
-  unsigned int i, argc, key_len, len;
-  unsigned char *tmp, channel_key[32];
-
-  SILC_LOG_DEBUG(("Start"));
+  unsigned int i, len;
+  unsigned char *tmp;
 
-  argc = silc_argument_get_arg_num(cmd->args);
-  if (argc < 1) {
-    silc_server_command_send_status_reply(cmd, SILC_COMMAND_LEAVE,
-                                         SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
-    goto out;
-  }
-  if (argc > 2) {
-    silc_server_command_send_status_reply(cmd, SILC_COMMAND_LEAVE,
-                                         SILC_STATUS_ERR_TOO_MANY_PARAMS);
-    goto out;
-  }
+  SILC_SERVER_COMMAND_CHECK_ARGC(SILC_COMMAND_LEAVE, cmd, 1, 2);
 
   /* Get Channel ID */
   tmp = silc_argument_get_arg_type(cmd->args, 1, &len);
@@ -2248,7 +2674,7 @@ SILC_SERVER_CMD_FUNC(leave)
   id = silc_id_payload_parse_id(tmp, len);
 
   /* Get channel entry */
-  channel = silc_idlist_find_channel_by_id(server->local_list, id);
+  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);
@@ -2266,7 +2692,7 @@ SILC_SERVER_CMD_FUNC(leave)
      of clients on the channel. */
   if (!server->standalone)
     silc_server_send_remove_channel_user(server, 
-                                        server->id_entry->router->connection,
+                                        server->router->connection,
                                         server->server_type == SILC_ROUTER ?
                                         TRUE : FALSE, id_entry->id, id);
 
@@ -2281,23 +2707,14 @@ SILC_SERVER_CMD_FUNC(leave)
     goto out;
 
   /* Re-generate channel key */
-  key_len = channel->key_len / 8;
-  for (i = 0; i < key_len; i++)
-    channel_key[i] = silc_rng_get_byte(server->rng);
-  channel->channel_key->cipher->set_key(channel->channel_key->context, 
-                                       channel_key, key_len);
-  memset(channel->key, 0, key_len);
-  silc_free(channel->key);
-  channel->key = silc_calloc(key_len, sizeof(*channel->key));
-  memcpy(channel->key, channel_key, key_len);
-  memset(channel_key, 0, sizeof(channel_key));
+  silc_server_create_channel_key(server, channel, 0);
 
   /* Encode channel key payload to be distributed on the channel */
   packet = 
     silc_channel_key_payload_encode(len, tmp,
                                    strlen(channel->channel_key->cipher->name),
                                    channel->channel_key->cipher->name,
-                                   key_len, channel->key);
+                                   channel->key_len / 8, channel->key);
 
   /* If we are normal server then we will send it to our router.  If we
      are router we will send it to all local servers that has clients on
@@ -2305,7 +2722,7 @@ SILC_SERVER_CMD_FUNC(leave)
   if (server->server_type == SILC_SERVER) {
     if (!server->standalone)
       silc_server_packet_send(server, 
-                             cmd->server->id_entry->router->connection,
+                             cmd->server->router->connection,
                              SILC_PACKET_CHANNEL_KEY, 0, packet->data,
                              packet->len, TRUE);
   } else {
@@ -2336,25 +2753,13 @@ SILC_SERVER_CMD_FUNC(names)
   SilcChannelClientEntry chl;
   SilcChannelID *id;
   SilcBuffer packet;
-  unsigned int i, len, len2, tmp_len, argc;
+  unsigned int i, len, len2, tmp_len;
   unsigned char *tmp;
   char *name_list = NULL, *n;
   SilcBuffer client_id_list;
   SilcBuffer client_mode_list;
 
-  SILC_LOG_DEBUG(("Start"));
-
-  argc = silc_argument_get_arg_num(cmd->args);
-  if (argc < 1) {
-    silc_server_command_send_status_reply(cmd, SILC_COMMAND_NAMES,
-                                         SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
-    goto out;
-  }
-  if (argc > 2) {
-    silc_server_command_send_status_reply(cmd, SILC_COMMAND_NAMES,
-                                         SILC_STATUS_ERR_TOO_MANY_PARAMS);
-    goto out;
-  }
+  SILC_SERVER_COMMAND_CHECK_ARGC(SILC_COMMAND_NAMES, cmd, 1, 2);
 
   /* Get Channel ID */
   tmp = silc_argument_get_arg_type(cmd->args, 1, &tmp_len);
@@ -2368,13 +2773,13 @@ SILC_SERVER_CMD_FUNC(names)
   /* Check whether the channel exists. If we are normal server and the
      channel does not exist we will send this same command to our router
      which will know if the channel exists. */
-  channel = silc_idlist_find_channel_by_id(server->local_list, id);
+  channel = silc_idlist_find_channel_by_id(server->local_list, id, NULL);
   if (!channel) {
     if (server->server_type == SILC_SERVER && !server->standalone) {
       /* XXX Send names command */
 
       cmd->pending = TRUE;
-      silc_server_command_pending(SILC_COMMAND_NAMES, 0,
+      silc_server_command_pending(server, SILC_COMMAND_NAMES, 0,
                                  silc_server_command_names, context);
       return;
     }