updates.
[silc.git] / lib / silcclient / command_reply.c
index c026a869182080503136569630e17f8d350278b1..d6247a822ff1e170c3759ebfa06f4debb216a7f7 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),
@@ -113,6 +113,7 @@ const SilcCommandStatusMessage silc_command_status_messages[] = {
   { STAT(BAD_NICKNAME),    "Bad nickname" },
   { STAT(BAD_CHANNEL),     "Bad channel name" },
   { STAT(AUTH_FAILED),     "Authentication failed" },
+  { STAT(UNKNOWN_ALGORITHM), "Unsupported algorithm" },
 
   { 0, NULL }
 };
@@ -626,10 +627,6 @@ SILC_CLIENT_CMD_REPLY_FUNC(invite)
   silc_client_command_reply_free(cmd);
 }
  
-SILC_CLIENT_CMD_REPLY_FUNC(quit)
-{
-}
-
 SILC_CLIENT_CMD_REPLY_FUNC(kill)
 {
 }
@@ -681,10 +678,6 @@ SILC_CLIENT_CMD_REPLY_FUNC(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)
@@ -742,22 +735,21 @@ SILC_CLIENT_CMD_REPLY_FUNC(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"));
 
@@ -770,7 +762,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;
@@ -804,14 +796,14 @@ SILC_CLIENT_CMD_REPLY_FUNC(join)
   }
 
   /* 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);
@@ -822,28 +814,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);
 
-  if (topic)
-    client->ops->say(cmd->client, conn, 
-                    "Topic for %s: %s", channel_name, topic);
+    /* 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;
+      if (client_entry == conn->local_entry)
+       continue;
+    }
+
+    /* 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);
+
+  /* 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_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);
@@ -1037,22 +1113,122 @@ SILC_CLIENT_CMD_REPLY_FUNC(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)
@@ -1239,49 +1415,8 @@ SILC_CLIENT_CMD_REPLY_FUNC(users)
     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_PENDING_EXEC(cmd, SILC_COMMAND_USERS);