updates.
[silc.git] / lib / silcclient / command_reply.c
index 85fa37da45556fccc6b3672eacffa64488cdce2a..014ba8ce39a141ef9391cfd99f911ec368d056bd 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
@@ -34,6 +34,7 @@
 /* $Id$ */
 
 #include "clientlibincludes.h"
+#include "client_internal.h"
 
 /* Client command reply list. */
 SilcClientCommandReply silc_command_reply_list[] =
@@ -45,7 +46,6 @@ SilcClientCommandReply silc_command_reply_list[] =
   SILC_CLIENT_CMD_REPLY(list, LIST),
   SILC_CLIENT_CMD_REPLY(topic, TOPIC),
   SILC_CLIENT_CMD_REPLY(invite, INVITE),
-  SILC_CLIENT_CMD_REPLY(quit, QUIT),
   SILC_CLIENT_CMD_REPLY(kill, KILL),
   SILC_CLIENT_CMD_REPLY(info, INFO),
   SILC_CLIENT_CMD_REPLY(connect, CONNECT),
@@ -59,7 +59,7 @@ SilcClientCommandReply silc_command_reply_list[] =
   SILC_CLIENT_CMD_REPLY(kick, KICK),
   SILC_CLIENT_CMD_REPLY(restart, RESTART),
   SILC_CLIENT_CMD_REPLY(close, CLOSE),
-  SILC_CLIENT_CMD_REPLY(die, DIE),
+  SILC_CLIENT_CMD_REPLY(shutdown, SHUTDOWN),
   SILC_CLIENT_CMD_REPLY(silcoper, SILCOPER),
   SILC_CLIENT_CMD_REPLY(leave, LEAVE),
   SILC_CLIENT_CMD_REPLY(users, USERS),
@@ -107,11 +107,13 @@ const SilcCommandStatusMessage silc_command_status_messages[] = {
   { STAT(UNKNOWN_MODE),    "Unknown mode" },
   { STAT(NOT_YOU),         "Cannot change mode for other users" },
   { STAT(NO_CHANNEL_PRIV), "Permission denied. You are not channel operator" },
+  { STAT(NO_CHANNEL_FOPRIV),"Permission denied. You are not channel founder" },
   { STAT(NO_SERVER_PRIV),  "Permission denied. You are not server operator" },
   { STAT(NO_ROUTER_PRIV),  "Permission denied. You are not SILC operator" },
   { STAT(BAD_NICKNAME),    "Bad nickname" },
   { STAT(BAD_CHANNEL),     "Bad channel name" },
   { STAT(AUTH_FAILED),     "Authentication failed" },
+  { STAT(UNKNOWN_ALGORITHM), "Unsupported algorithm" },
 
   { 0, NULL }
 };
@@ -205,20 +207,18 @@ void silc_client_command_reply_free(SilcClientCommandReplyContext cmd)
 }
 
 static void 
-silc_client_command_reply_whois_print(SilcClientCommandReplyContext cmd,
-                                     SilcCommandStatus status)
+silc_client_command_reply_whois_save(SilcClientCommandReplyContext cmd,
+                                    SilcCommandStatus status)
 {
-  char buf[256];
-  int argc, len;
-  unsigned char *id_data;
-  char *nickname = NULL, *username = NULL;
-  char *realname = NULL;
+  SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
   SilcClientID *client_id;
   SilcIDCacheEntry id_cache = NULL;
   SilcClientEntry client_entry = NULL;
-  SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
-  
-  memset(buf, 0, sizeof(buf));
+  int argc, len;
+  unsigned char *id_data, *tmp;
+  char *nickname = NULL, *username = NULL;
+  char *realname = NULL;
+  unsigned int idle = 0;
   
   argc = silc_argument_get_arg_num(cmd->args);
 
@@ -229,28 +229,28 @@ silc_client_command_reply_whois_print(SilcClientCommandReplyContext cmd,
   }
   
   client_id = silc_id_payload_parse_id(id_data, len);
-  
-  nickname = silc_argument_get_arg_type(cmd->args, 3, &len);
-  if (nickname) {
-    strncat(buf, nickname, len);
-    strncat(buf, " is ", 4);
+  if (!client_id) {
+    COMMAND_REPLY_ERROR;
+    return;
   }
   
+  nickname = silc_argument_get_arg_type(cmd->args, 3, &len);
   username = silc_argument_get_arg_type(cmd->args, 4, &len);
-  if (username) {
-    strncat(buf, username, len);
-  }
-  
   realname = silc_argument_get_arg_type(cmd->args, 5, &len);
-  if (realname) {
-    strncat(buf, " (", 2);
-    strncat(buf, realname, len);
-    strncat(buf, ")", 1);
+  if (!nickname || !username || !realname) {
+    COMMAND_REPLY_ERROR;
+    return;
   }
-  
+
+  tmp = silc_argument_get_arg_type(cmd->args, 7, &len);
+  if (tmp)
+    SILC_GET32_MSB(idle, tmp);
+
   /* Check if we have this client cached already. */
   if (!silc_idcache_find_by_id_one(conn->client_cache, (void *)client_id,
                                   SILC_ID_CLIENT, &id_cache)) {
+    SILC_LOG_DEBUG(("Adding new client entry"));
+
     client_entry = silc_calloc(1, sizeof(*client_entry));
     client_entry->id = client_id;
     silc_parse_nickname(nickname, &client_entry->nickname, 
@@ -273,6 +273,8 @@ silc_client_command_reply_whois_print(SilcClientCommandReplyContext cmd,
     if (client_entry->realname)
       silc_free(client_entry->realname);
 
+    SILC_LOG_DEBUG(("Updating client entry"));
+
     silc_parse_nickname(nickname, &client_entry->nickname, 
                        &client_entry->server, &client_entry->num);
     client_entry->username = strdup(username);
@@ -280,16 +282,15 @@ silc_client_command_reply_whois_print(SilcClientCommandReplyContext cmd,
       client_entry->realname = strdup(realname);
 
     id_cache->data = client_entry->nickname;
+    silc_idcache_sort_by_data(conn->client_cache);
 
     silc_free(client_id);
   }
 
-  if (!cmd->callback)
-    cmd->client->ops->say(cmd->client, conn, "%s", buf);
-
   /* Notify application */
-  COMMAND_REPLY((ARGS, client_entry, nickname, username, realname, 
-                NULL, NULL));
+  if (!cmd->callback)
+    COMMAND_REPLY((ARGS, client_entry, nickname, username, realname, 
+                  NULL, idle));
 }
 
 /* Received reply for WHOIS command. This maybe called several times
@@ -330,27 +331,27 @@ SILC_CLIENT_CMD_REPLY_FUNC(whois)
   }
 
   /* Display one whois reply */
-  if (status == SILC_STATUS_OK) {
-    silc_client_command_reply_whois_print(cmd, status);
-  }
+  if (status == SILC_STATUS_OK)
+    silc_client_command_reply_whois_save(cmd, status);
 
-  /* XXX list should not be displayed untill all items has been received. */
-  if (status == SILC_STATUS_LIST_START) {
-    silc_client_command_reply_whois_print(cmd, status);
-  }
+  /* List */
+  if (status == SILC_STATUS_LIST_START ||
+      status == SILC_STATUS_LIST_ITEM ||
+      status == SILC_STATUS_LIST_END)
+    silc_client_command_reply_whois_save(cmd, status);
 
-  if (status == SILC_STATUS_LIST_ITEM) {
-    silc_client_command_reply_whois_print(cmd, status);
-  }
-
-  if (status == SILC_STATUS_LIST_END) {
-    silc_client_command_reply_whois_print(cmd, status);
+  /* Pending callbacks are not executed if this was an list entry */
+  if (status != SILC_STATUS_OK &&
+      status != SILC_STATUS_LIST_END) {
+    silc_client_command_reply_free(cmd);
+    return;
   }
 
   /* Execute any pending command callbacks */
-  SILC_CLIENT_COMMAND_EXEC_PENDING(cmd, SILC_COMMAND_WHOIS);
+  SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_WHOIS);
 
  out:
+  SILC_CLIENT_PENDING_DESTRUCTOR(cmd, SILC_COMMAND_WHOIS);
   silc_client_command_reply_free(cmd);
 }
 
@@ -361,6 +362,77 @@ SILC_CLIENT_CMD_REPLY_FUNC(whowas)
 
 }
 
+static void 
+silc_client_command_reply_identify_save(SilcClientCommandReplyContext cmd,
+                                       SilcCommandStatus status)
+{
+  SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
+  SilcClientID *client_id;
+  SilcIDCacheEntry id_cache = NULL;
+  SilcClientEntry client_entry = NULL;
+  int argc, len;
+  unsigned char *id_data;
+  char *nickname = NULL, *username = NULL;
+  
+  argc = silc_argument_get_arg_num(cmd->args);
+
+  id_data = silc_argument_get_arg_type(cmd->args, 2, &len);
+  if (!id_data) {
+    COMMAND_REPLY_ERROR;
+    return;
+  }
+  
+  client_id = silc_id_payload_parse_id(id_data, len);
+  if (!client_id) {
+    COMMAND_REPLY_ERROR;
+    return;
+  }
+  
+  nickname = silc_argument_get_arg_type(cmd->args, 3, &len);
+  username = silc_argument_get_arg_type(cmd->args, 4, &len);
+
+  /* Check if we have this client cached already. */
+  if (!silc_idcache_find_by_id_one(conn->client_cache, (void *)client_id,
+                                  SILC_ID_CLIENT, &id_cache)) {
+    SILC_LOG_DEBUG(("Adding new client entry"));
+
+    client_entry = silc_calloc(1, sizeof(*client_entry));
+    client_entry->id = client_id;
+    silc_parse_nickname(nickname, &client_entry->nickname, 
+                       &client_entry->server, &client_entry->num);
+    if (username)
+      client_entry->username = strdup(username);
+    
+    /* Add client to cache */
+    silc_idcache_add(conn->client_cache, client_entry->nickname,
+                    SILC_ID_CLIENT, client_id, (void *)client_entry, TRUE);
+  } else {
+    client_entry = (SilcClientEntry)id_cache->context;
+    if (client_entry->nickname)
+      silc_free(client_entry->nickname);
+    if (client_entry->server)
+      silc_free(client_entry->server);
+    if (username && client_entry->username)
+      silc_free(client_entry->username);
+    
+    SILC_LOG_DEBUG(("Updating client entry"));
+
+    silc_parse_nickname(nickname, &client_entry->nickname, 
+                       &client_entry->server, &client_entry->num);
+    
+    if (username)
+      client_entry->username = strdup(username);
+    
+    id_cache->data = client_entry->nickname;
+    silc_idcache_sort_by_data(conn->client_cache);
+    
+    silc_free(client_id);
+  }
+
+  /* Notify application */
+  COMMAND_REPLY((ARGS, client_entry, nickname, username));
+}
+
 /* Received reply for IDENTIFY command. This maybe called several times
    for one IDENTIFY command as server may reply with list of results. 
    This is totally silent and does not print anything on screen. */
@@ -369,8 +441,6 @@ SILC_CLIENT_CMD_REPLY_FUNC(identify)
 {
   SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
   SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
-  SilcClientEntry client_entry;
-  SilcIDCacheEntry id_cache = NULL;
   SilcCommandStatus status;
   unsigned char *tmp;
 
@@ -378,7 +448,10 @@ SILC_CLIENT_CMD_REPLY_FUNC(identify)
 
   tmp = silc_argument_get_arg_type(cmd->args, 1, NULL);
   SILC_GET16_MSB(status, tmp);
-  if (status != SILC_STATUS_OK) {
+  if (status != SILC_STATUS_OK && 
+      status != SILC_STATUS_LIST_START &&
+      status != SILC_STATUS_LIST_ITEM &&
+      status != SILC_STATUS_LIST_END) {
     if (status == SILC_STATUS_ERR_NO_SUCH_NICK) {
       /* Take nickname which may be provided */
       tmp = silc_argument_get_arg_type(cmd->args, 3, NULL);
@@ -398,67 +471,28 @@ SILC_CLIENT_CMD_REPLY_FUNC(identify)
     }
   }
 
-  /* Display one whois reply */
-  if (status == SILC_STATUS_OK) {
-    unsigned int len;
-    unsigned char *id_data;
-    char *nickname;
-    char *username;
-    SilcClientID *client_id;
-
-    id_data = silc_argument_get_arg_type(cmd->args, 2, &len);
-    if (!id_data)
-      goto out;
-    client_id = silc_id_payload_parse_id(id_data, len);
-
-    nickname = silc_argument_get_arg_type(cmd->args, 3, NULL);
-    username = silc_argument_get_arg_type(cmd->args, 4, NULL);
-
-    if (!silc_idcache_find_by_id_one(conn->client_cache, (void *)client_id,
-                                    SILC_ID_CLIENT, &id_cache)) {
-      client_entry = silc_calloc(1, sizeof(*client_entry));
-      client_entry->id = client_id;
-      silc_parse_nickname(nickname, &client_entry->nickname, 
-                         &client_entry->server, &client_entry->num);
-      if (username)
-       client_entry->username = strdup(username);
-    
-      /* Add client to cache */
-      silc_idcache_add(conn->client_cache, client_entry->nickname,
-                      SILC_ID_CLIENT, client_id, (void *)client_entry, TRUE);
-    } else {
-      client_entry = (SilcClientEntry)id_cache->context;
-      if (client_entry->nickname)
-       silc_free(client_entry->nickname);
-      if (client_entry->server)
-       silc_free(client_entry->server);
-      if (username && client_entry->username)
-       silc_free(client_entry->username);
-
-      silc_parse_nickname(nickname, &client_entry->nickname, 
-                         &client_entry->server, &client_entry->num);
-
-      if (username)
-       client_entry->username = strdup(username);
-
-      id_cache->data = client_entry->nickname;
-
-      silc_free(client_id);
-    }
-  }
-
-  if (status == SILC_STATUS_LIST_START) {
+  /* Save one IDENTIFY entry */
+  if (status == SILC_STATUS_OK)
+    silc_client_command_reply_identify_save(cmd, status);
 
-  }
-
-  if (status == SILC_STATUS_LIST_END) {
+  /* List */
+  if (status == SILC_STATUS_LIST_START ||
+      status == SILC_STATUS_LIST_ITEM ||
+      status == SILC_STATUS_LIST_END)
+    silc_client_command_reply_identify_save(cmd, status);
 
+  /* Pending callbacks are not executed if this was an list entry */
+  if (status != SILC_STATUS_OK &&
+      status != SILC_STATUS_LIST_END) {
+    silc_client_command_reply_free(cmd);
+    return;
   }
 
   /* Execute any pending command callbacks */
-  SILC_CLIENT_COMMAND_EXEC_PENDING(cmd, SILC_COMMAND_IDENTIFY);
+  SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_IDENTIFY);
 
  out:
+  SILC_CLIENT_PENDING_DESTRUCTOR(cmd, SILC_COMMAND_IDENTIFY);
   silc_client_command_reply_free(cmd);
 }
 
@@ -495,15 +529,20 @@ SILC_CLIENT_CMD_REPLY_FUNC(nick)
   /* Take received Client ID */
   tmp = silc_argument_get_arg_type(cmd->args, 2, &len);
   idp = silc_id_payload_parse_data(tmp, len);
+  if (!idp) {
+    COMMAND_REPLY_ERROR;
+    goto out;
+  }
   silc_client_receive_new_id(cmd->client, cmd->sock, idp);
     
   /* Notify application */
   COMMAND_REPLY((ARGS, conn->local_entry));
 
   /* Execute any pending command callbacks */
-  SILC_CLIENT_COMMAND_EXEC_PENDING(cmd, SILC_COMMAND_NICK);
+  SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_NICK);
 
  out:
+  SILC_CLIENT_PENDING_DESTRUCTOR(cmd, SILC_COMMAND_NICK);
   silc_client_command_reply_free(cmd);
 }
 
@@ -530,6 +569,7 @@ SILC_CLIENT_CMD_REPLY_FUNC(topic)
     cmd->client->ops->say(cmd->client, conn,
             "%s", silc_client_command_status_message(status));
     COMMAND_REPLY_ERROR;
+    SILC_CLIENT_PENDING_DESTRUCTOR(cmd, SILC_COMMAND_TOPIC);
     silc_client_command_reply_free(cmd);
     return;
   }
@@ -551,6 +591,8 @@ SILC_CLIENT_CMD_REPLY_FUNC(topic)
     goto out;
 
   channel_id = silc_id_payload_parse_id(tmp, len);
+  if (!channel_id)
+    goto out;
 
   /* Get the channel name */
   if (!silc_idcache_find_by_id_one(conn->channel_cache, (void *)channel_id,
@@ -570,9 +612,10 @@ SILC_CLIENT_CMD_REPLY_FUNC(topic)
   COMMAND_REPLY((ARGS, channel, topic));
 
   /* Execute any pending command callbacks */
-  SILC_CLIENT_COMMAND_EXEC_PENDING(cmd, SILC_COMMAND_TOPIC);
+  SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_TOPIC);
 
  out:
+  SILC_CLIENT_PENDING_DESTRUCTOR(cmd, SILC_COMMAND_TOPIC);
   silc_client_command_reply_free(cmd);
 }
 
@@ -591,6 +634,7 @@ SILC_CLIENT_CMD_REPLY_FUNC(invite)
     cmd->client->ops->say(cmd->client, conn,
             "%s", silc_client_command_status_message(status));
     COMMAND_REPLY_ERROR;
+    SILC_CLIENT_PENDING_DESTRUCTOR(cmd, SILC_COMMAND_INVITE);
     silc_client_command_reply_free(cmd);
     return;
   }
@@ -599,15 +643,12 @@ SILC_CLIENT_CMD_REPLY_FUNC(invite)
   COMMAND_REPLY((ARGS));
 
   /* Execute any pending command callbacks */
-  SILC_CLIENT_COMMAND_EXEC_PENDING(cmd, SILC_COMMAND_INVITE);
+  SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_INVITE);
 
+  SILC_CLIENT_PENDING_DESTRUCTOR(cmd, SILC_COMMAND_INVITE);
   silc_client_command_reply_free(cmd);
 }
  
-SILC_CLIENT_CMD_REPLY_FUNC(quit)
-{
-}
-
 SILC_CLIENT_CMD_REPLY_FUNC(kill)
 {
 }
@@ -629,6 +670,7 @@ SILC_CLIENT_CMD_REPLY_FUNC(info)
     cmd->client->ops->say(cmd->client, conn,
             "%s", silc_client_command_status_message(status));
     COMMAND_REPLY_ERROR;
+    SILC_CLIENT_PENDING_DESTRUCTOR(cmd, SILC_COMMAND_INFO);
     silc_client_command_reply_free(cmd);
     return;
   }
@@ -651,16 +693,13 @@ SILC_CLIENT_CMD_REPLY_FUNC(info)
   COMMAND_REPLY((ARGS, NULL, (char *)tmp));
 
   /* Execute any pending command callbacks */
-  SILC_CLIENT_COMMAND_EXEC_PENDING(cmd, SILC_COMMAND_INFO);
+  SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_INFO);
 
  out:
+  SILC_CLIENT_PENDING_DESTRUCTOR(cmd, SILC_COMMAND_INFO);
   silc_client_command_reply_free(cmd);
 }
 
-SILC_CLIENT_CMD_REPLY_FUNC(connect)
-{
-}
-
 /* Received reply to PING command. The reply time is shown to user. */
 
 SILC_CLIENT_CMD_REPLY_FUNC(ping)
@@ -681,7 +720,12 @@ SILC_CLIENT_CMD_REPLY_FUNC(ping)
   }
 
   curtime = time(NULL);
-  id = silc_id_str2id(cmd->packet->src_id, cmd->packet->src_id_type);
+  id = silc_id_str2id(cmd->packet->src_id, cmd->packet->src_id_len,
+                     cmd->packet->src_id_type);
+  if (!id) {
+    COMMAND_REPLY_ERROR;
+    goto out;
+  }
 
   for (i = 0; i < conn->ping_count; i++) {
     if (!SILC_ID_SERVER_COMPARE(conn->ping[i].dest_id, id)) {
@@ -690,44 +734,44 @@ SILC_CLIENT_CMD_REPLY_FUNC(ping)
                            "Ping reply from %s: %d second%s", 
                            conn->ping[i].dest_name, diff, 
                            diff == 1 ? "" : "s");
-
+      
       conn->ping[i].start_time = 0;
       silc_free(conn->ping[i].dest_id);
       conn->ping[i].dest_id = NULL;
       silc_free(conn->ping[i].dest_name);
       conn->ping[i].dest_name = NULL;
-
-      /* Notify application */
-      COMMAND_REPLY((ARGS));
       break;
     }
   }
 
   silc_free(id);
 
+  /* Notify application */
+  COMMAND_REPLY((ARGS));
+
   /* Execute any pending command callbacks */
-  SILC_CLIENT_COMMAND_EXEC_PENDING(cmd, SILC_COMMAND_PING);
+  SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_PING);
 
  out:
+  SILC_CLIENT_PENDING_DESTRUCTOR(cmd, SILC_COMMAND_PING);
   silc_client_command_reply_free(cmd);
 }
 
-SILC_CLIENT_CMD_REPLY_FUNC(oper)
-{
-}
-
 /* Received reply for JOIN command. */
 
 SILC_CLIENT_CMD_REPLY_FUNC(join)
 {
   SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
   SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
-  SilcClient client = cmd->client;
   SilcCommandStatus status;
   SilcIDPayload idp = NULL;
-  unsigned int argc, mode, len;
-  char *topic, *tmp, *channel_name = NULL;
-  SilcBuffer keyp;
+  SilcChannelEntry channel;
+  SilcIDCacheEntry id_cache = NULL;
+  SilcChannelUser chu;
+  unsigned int argc, mode, len, list_count;
+  char *topic, *tmp, *channel_name = NULL, *hmac;
+  SilcBuffer keyp, client_id_list, client_mode_list;
+  int i;
 
   SILC_LOG_DEBUG(("Start"));
 
@@ -740,7 +784,7 @@ SILC_CLIENT_CMD_REPLY_FUNC(join)
   }
 
   argc = silc_argument_get_arg_num(cmd->args);
-  if (argc < 3 || argc > 9) {
+  if (argc < 7 || argc > 14) {
     cmd->client->ops->say(cmd->client, conn,
             "Cannot join channel: Bad reply packet");
     COMMAND_REPLY_ERROR;
@@ -767,16 +811,21 @@ SILC_CLIENT_CMD_REPLY_FUNC(join)
     goto out;
   }
   idp = silc_id_payload_parse_data(tmp, len);
+  if (!idp) {
+    COMMAND_REPLY_ERROR;
+    silc_free(channel_name);
+    goto out;
+  }
 
   /* Get channel mode */
-  tmp = silc_argument_get_arg_type(cmd->args, 4, NULL);
+  tmp = silc_argument_get_arg_type(cmd->args, 5, NULL);
   if (tmp)
     SILC_GET32_MSB(mode, tmp);
   else
     mode = 0;
 
   /* Get channel key */
-  tmp = silc_argument_get_arg_type(cmd->args, 6, &len);
+  tmp = silc_argument_get_arg_type(cmd->args, 7, &len);
   if (!tmp) {
     silc_id_payload_free(idp);
     silc_free(channel_name);
@@ -787,29 +836,112 @@ SILC_CLIENT_CMD_REPLY_FUNC(join)
   silc_buffer_put(keyp, tmp, len);
 
   /* Get topic */
-  topic = silc_argument_get_arg_type(cmd->args, 8, NULL);
+  topic = silc_argument_get_arg_type(cmd->args, 10, NULL);
 
-  /* Save received Channel ID */
-  silc_client_new_channel_id(cmd->client, cmd->sock, channel_name, 
-                            mode, idp);
+  /* Save received Channel ID. This actually creates the channel */
+  channel = silc_client_new_channel_id(cmd->client, cmd->sock, channel_name, 
+                                      mode, idp);
   silc_id_payload_free(idp);
 
-  /* Save channel key */
-  silc_client_save_channel_key(conn, keyp, conn->current_channel);
-  silc_buffer_free(keyp);
+  /* Get hmac */
+  hmac = silc_argument_get_arg_type(cmd->args, 11, NULL);
+  if (hmac) {
+    if (!silc_hmac_alloc(hmac, NULL, &channel->hmac)) {
+      cmd->client->ops->say(cmd->client, conn, 
+                           "Cannot join channel: Unsupported HMAC `%s'",
+                           hmac);
+      COMMAND_REPLY_ERROR;
+      silc_free(channel_name);
+      goto out;
+    }
+  }
+
+  /* Get the list count */
+  tmp = silc_argument_get_arg_type(cmd->args, 12, &len);
+  if (!tmp)
+    goto out;
+  SILC_GET32_MSB(list_count, tmp);
+
+  /* Get Client ID list */
+  tmp = silc_argument_get_arg_type(cmd->args, 13, &len);
+  if (!tmp)
+    goto out;
+
+  client_id_list = silc_buffer_alloc(len);
+  silc_buffer_pull_tail(client_id_list, len);
+  silc_buffer_put(client_id_list, tmp, len);
+
+  /* Get client mode list */
+  tmp = silc_argument_get_arg_type(cmd->args, 14, &len);
+  if (!tmp)
+    goto out;
+
+  client_mode_list = silc_buffer_alloc(len);
+  silc_buffer_pull_tail(client_mode_list, len);
+  silc_buffer_put(client_mode_list, tmp, len);
+
+  /* Add clients we received in the reply to the channel */
+  for (i = 0; i < list_count; i++) {
+    unsigned short idp_len;
+    unsigned int mode;
+    SilcClientID *client_id;
+    SilcClientEntry client_entry;
+
+    /* Client ID */
+    SILC_GET16_MSB(idp_len, client_id_list->data + 2);
+    idp_len += 4;
+    client_id = silc_id_payload_parse_id(client_id_list->data, idp_len);
+    if (!client_id)
+      continue;
+
+    /* Mode */
+    SILC_GET32_MSB(mode, client_mode_list->data);
+
+    /* Check if we have this client cached already. */
+    if (!silc_idcache_find_by_id_one(conn->client_cache, (void *)client_id,
+                                    SILC_ID_CLIENT, &id_cache)) {
+      /* No, we don't have it, add entry for it. */
+      client_entry = silc_calloc(1, sizeof(*client_entry));
+      client_entry->id = silc_id_dup(client_id, SILC_ID_CLIENT);
+      silc_idcache_add(conn->client_cache, NULL, SILC_ID_CLIENT, 
+                      client_entry->id, (void *)client_entry, FALSE);
+    } else {
+      /* Yes, we have it already */
+      client_entry = (SilcClientEntry)id_cache->context;
+    }
+
+    /* Join the client to the channel */
+    chu = silc_calloc(1, sizeof(*chu));
+    chu->client = client_entry;
+    chu->mode = mode;
+    silc_list_add(channel->clients, chu);
+    silc_free(client_id);
+
+    silc_buffer_pull(client_id_list, idp_len);
+    silc_buffer_pull(client_mode_list, 4);
+  }
+  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);
 
-  if (topic)
-    client->ops->say(cmd->client, conn, 
-                    "Topic for %s: %s", channel_name, topic);
+  /* Save channel key */
+  silc_client_save_channel_key(conn, keyp, channel);
 
   /* Notify application */
-  COMMAND_REPLY((ARGS, channel_name, conn->current_channel, mode,
-                NULL, NULL, topic));
+  COMMAND_REPLY((ARGS, channel_name, channel, mode, 0, keyp->head, NULL,
+                NULL, topic, hmac, list_count, client_id_list, 
+                client_mode_list));
 
   /* Execute any pending command callbacks */
-  SILC_CLIENT_COMMAND_EXEC_PENDING(cmd, SILC_COMMAND_JOIN);
+  SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_JOIN);
+
+  silc_buffer_free(keyp);
+  silc_buffer_free(client_id_list);
+  silc_buffer_free(client_mode_list);
 
  out:
+  SILC_CLIENT_PENDING_DESTRUCTOR(cmd, SILC_COMMAND_JOIN);
   silc_client_command_reply_free(cmd);
 }
 
@@ -870,9 +1002,10 @@ SILC_CLIENT_CMD_REPLY_FUNC(motd)
   COMMAND_REPLY((ARGS, motd));
 
   /* Execute any pending command callbacks */
-  SILC_CLIENT_COMMAND_EXEC_PENDING(cmd, SILC_COMMAND_MOTD);
+  SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_MOTD);
 
  out:
+  SILC_CLIENT_PENDING_DESTRUCTOR(cmd, SILC_COMMAND_MOTD);
   silc_client_command_reply_free(cmd);
 }
 
@@ -908,9 +1041,10 @@ SILC_CLIENT_CMD_REPLY_FUNC(cmode)
   COMMAND_REPLY((ARGS, tmp));
 
   /* Execute any pending command callbacks */
-  SILC_CLIENT_COMMAND_EXEC_PENDING(cmd, SILC_COMMAND_CMODE);
+  SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_CMODE);
 
  out:
+  SILC_CLIENT_PENDING_DESTRUCTOR(cmd, SILC_COMMAND_CMODE);
   silc_client_command_reply_free(cmd);
 }
 
@@ -948,6 +1082,10 @@ SILC_CLIENT_CMD_REPLY_FUNC(cumode)
     goto out;
   }
   client_id = silc_id_payload_parse_id(id, len);
+  if (!client_id) {
+    COMMAND_REPLY_ERROR;
+    goto out;
+  }
   
   /* Get client entry */
   if (!silc_idcache_find_by_id_one(conn->client_cache, (void *)client_id,
@@ -961,32 +1099,156 @@ SILC_CLIENT_CMD_REPLY_FUNC(cumode)
   silc_free(client_id);
   
   /* Execute any pending command callbacks */
-  SILC_CLIENT_COMMAND_EXEC_PENDING(cmd, SILC_COMMAND_CUMODE);
+  SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_CUMODE);
 
  out:
+  SILC_CLIENT_PENDING_DESTRUCTOR(cmd, SILC_COMMAND_CUMODE);
   silc_client_command_reply_free(cmd);
 }
 
 SILC_CLIENT_CMD_REPLY_FUNC(kick)
 {
+  SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
+  SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
+  SilcCommandStatus status;
+  unsigned char *tmp;
+
+  tmp = silc_argument_get_arg_type(cmd->args, 1, NULL);
+  SILC_GET16_MSB(status, tmp);
+  if (status != SILC_STATUS_OK) {
+    cmd->client->ops->say(cmd->client, conn,
+            "%s", silc_client_command_status_message(status));
+    COMMAND_REPLY_ERROR;
+    goto out;
+  }
+
+  /* Notify application */
+  COMMAND_REPLY((ARGS));
+
+  /* Execute any pending command callbacks */
+  SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_KICK);
+
+ out:
+  SILC_CLIENT_PENDING_DESTRUCTOR(cmd, SILC_COMMAND_KICK);
+  silc_client_command_reply_free(cmd);
+}
+
+SILC_CLIENT_CMD_REPLY_FUNC(silcoper)
+{
+}
+
+SILC_CLIENT_CMD_REPLY_FUNC(oper)
+{
+}
+
+SILC_CLIENT_CMD_REPLY_FUNC(connect)
+{
+  SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
+  SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
+  SilcCommandStatus status;
+  unsigned char *tmp;
+
+  tmp = silc_argument_get_arg_type(cmd->args, 1, NULL);
+  SILC_GET16_MSB(status, tmp);
+  if (status != SILC_STATUS_OK) {
+    cmd->client->ops->say(cmd->client, conn,
+            "%s", silc_client_command_status_message(status));
+    COMMAND_REPLY_ERROR;
+    goto out;
+  }
+
+  /* Notify application */
+  COMMAND_REPLY((ARGS));
+
+  /* Execute any pending command callbacks */
+  SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_CONNECT);
+
+ out:
+  SILC_CLIENT_PENDING_DESTRUCTOR(cmd, SILC_COMMAND_CONNECT);
+  silc_client_command_reply_free(cmd);
 }
 
 SILC_CLIENT_CMD_REPLY_FUNC(restart)
 {
+  SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
+  SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
+  SilcCommandStatus status;
+  unsigned char *tmp;
+
+  tmp = silc_argument_get_arg_type(cmd->args, 1, NULL);
+  SILC_GET16_MSB(status, tmp);
+  if (status != SILC_STATUS_OK) {
+    cmd->client->ops->say(cmd->client, conn,
+            "%s", silc_client_command_status_message(status));
+    COMMAND_REPLY_ERROR;
+    goto out;
+  }
+
+  /* Notify application */
+  COMMAND_REPLY((ARGS));
+
+  /* Execute any pending command callbacks */
+  SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_RESTART);
+
+ out:
+  SILC_CLIENT_PENDING_DESTRUCTOR(cmd, SILC_COMMAND_RESTART);
+  silc_client_command_reply_free(cmd);
 }
  
 SILC_CLIENT_CMD_REPLY_FUNC(close)
 {
+  SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
+  SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
+  SilcCommandStatus status;
+  unsigned char *tmp;
+
+  tmp = silc_argument_get_arg_type(cmd->args, 1, NULL);
+  SILC_GET16_MSB(status, tmp);
+  if (status != SILC_STATUS_OK) {
+    cmd->client->ops->say(cmd->client, conn,
+            "%s", silc_client_command_status_message(status));
+    COMMAND_REPLY_ERROR;
+    goto out;
+  }
+
+  /* Notify application */
+  COMMAND_REPLY((ARGS));
+
+  /* Execute any pending command callbacks */
+  SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_CLOSE);
+
+ out:
+  SILC_CLIENT_PENDING_DESTRUCTOR(cmd, SILC_COMMAND_CLOSE);
+  silc_client_command_reply_free(cmd);
 }
  
-SILC_CLIENT_CMD_REPLY_FUNC(die)
+SILC_CLIENT_CMD_REPLY_FUNC(shutdown)
 {
+  SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
+  SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
+  SilcCommandStatus status;
+  unsigned char *tmp;
+
+  tmp = silc_argument_get_arg_type(cmd->args, 1, NULL);
+  SILC_GET16_MSB(status, tmp);
+  if (status != SILC_STATUS_OK) {
+    cmd->client->ops->say(cmd->client, conn,
+            "%s", silc_client_command_status_message(status));
+    COMMAND_REPLY_ERROR;
+    goto out;
+  }
+
+  /* Notify application */
+  COMMAND_REPLY((ARGS));
+
+  /* Execute any pending command callbacks */
+  SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_SHUTDOWN);
+
+ out:
+  SILC_CLIENT_PENDING_DESTRUCTOR(cmd, SILC_COMMAND_SHUTDOWN);
+  silc_client_command_reply_free(cmd);
 }
  
-SILC_CLIENT_CMD_REPLY_FUNC(silcoper)
-{
-}
-
 /* Reply to LEAVE command. */
 
 SILC_CLIENT_CMD_REPLY_FUNC(leave)
@@ -1002,15 +1264,17 @@ SILC_CLIENT_CMD_REPLY_FUNC(leave)
     cmd->client->ops->say(cmd->client, conn,
             "%s", silc_client_command_status_message(status));
     COMMAND_REPLY_ERROR;
-    return;
+    goto out;
   }
 
   /* Notify application */
   COMMAND_REPLY((ARGS));
 
   /* Execute any pending command callbacks */
-  SILC_CLIENT_COMMAND_EXEC_PENDING(cmd, SILC_COMMAND_LEAVE);
+  SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_LEAVE);
 
+ out:
+  SILC_CLIENT_PENDING_DESTRUCTOR(cmd, SILC_COMMAND_LEAVE);
   silc_client_command_reply_free(cmd);
 }
 
@@ -1050,6 +1314,8 @@ SILC_CLIENT_CMD_REPLY_FUNC(users)
   if (!tmp)
     goto out;
   channel_id = silc_id_payload_parse_id(tmp, tmp_len);
+  if (!channel_id)
+    goto out;
 
   /* Get the list count */
   tmp = silc_argument_get_arg_type(cmd->args, 3, &tmp_len);
@@ -1103,6 +1369,8 @@ SILC_CLIENT_CMD_REPLY_FUNC(users)
     SILC_GET16_MSB(idp_len, client_id_list->data + 2);
     idp_len += 4;
     client_id = silc_id_payload_parse_id(client_id_list->data, idp_len);
+    if (!client_id)
+      continue;
 
     /* Mode */
     SILC_GET32_MSB(mode, client_mode_list->data);
@@ -1155,66 +1423,23 @@ SILC_CLIENT_CMD_REPLY_FUNC(users)
        command reply we will reprocess this command reply by re-calling this
        USERS command reply callback. */
     silc_client_command_pending(conn, SILC_COMMAND_IDENTIFY, conn->cmd_ident,
-                               silc_client_command_reply_users, cmd);
+                               NULL, silc_client_command_reply_users, cmd);
 
     silc_buffer_free(res_cmd);
     if (channel_id)
       silc_free(channel_id);
 
-    for (i = 0; i < res_argc; i++)
-      silc_free(res_argv[i]);
     silc_free(res_argv);
     silc_free(res_argv_lens);
     silc_free(res_argv_types);
     return;
   }
 
-  /* We have all the clients on the channel cached now. Create a nice
-     output for user interface and notify application. */
-
-  if (!cmd->callback) {
-    /* Server has sent us USERS reply even when we haven't actually sent
-       USERS command. This is normal behaviour when joining to a channel.
-       Display some nice information on the user interface. */
-    int k = 0, len1 = 0, len2 = 0;
-    char *name_list = NULL;
-
-    silc_list_start(channel->clients);
-    while ((chu = silc_list_get(channel->clients)) != SILC_LIST_END) {
-      char *m, *n = chu->client->nickname;
-      len2 = strlen(n);
-      len1 += len2;
-
-      name_list = silc_realloc(name_list, sizeof(*name_list) * (len1 + 3));
-
-      m = silc_client_chumode_char(chu->mode);
-      if (m) {
-       memcpy(name_list + (len1 - len2), m, strlen(m));
-       len1 += strlen(m);
-       silc_free(m);
-      }
-
-      memcpy(name_list + (len1 - len2), n, len2);
-      name_list[len1] = 0;
-      
-      if (k == silc_list_count(channel->clients) - 1)
-       break;
-      memcpy(name_list + len1, " ", 1);
-      len1++;
-      k++;
-    }
-
-    cmd->client->ops->say(cmd->client, conn, "Users on %s: %s",
-                         channel->channel_name, name_list);
-    silc_free(name_list);
-  }
-
   /* Notify application */
-  COMMAND_REPLY((ARGS, channel, client_id_list->head,
-                client_mode_list->head));
+  COMMAND_REPLY((ARGS, channel, list_count, client_id_list, client_mode_list));
 
   /* Execute any pending command callbacks */
-  SILC_CLIENT_COMMAND_EXEC_PENDING(cmd, SILC_COMMAND_USERS);
+  SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_USERS);
 
   silc_buffer_free(client_id_list);
   silc_buffer_free(client_mode_list);
@@ -1222,5 +1447,6 @@ SILC_CLIENT_CMD_REPLY_FUNC(users)
  out:
   if (channel_id)
     silc_free(channel_id);
+  SILC_CLIENT_PENDING_DESTRUCTOR(cmd, SILC_COMMAND_USERS);
   silc_client_command_reply_free(cmd);
 }