Router environment. WHOIS/IDENTIFY/JOIN/channel message sending
authorPekka Riikonen <priikone@silcnet.org>
Sat, 27 Jan 2001 23:11:10 +0000 (23:11 +0000)
committerPekka Riikonen <priikone@silcnet.org>
Sat, 27 Jan 2001 23:11:10 +0000 (23:11 +0000)
should now summat work in router environment.

14 files changed:
CHANGES
apps/silcd/command.c
apps/silcd/command_reply.c
apps/silcd/idlist.c
apps/silcd/packet_receive.c
apps/silcd/packet_send.c
apps/silcd/server.c
apps/silcd/server.h
doc/draft-riikonen-silc-pp-01.nroff
doc/draft-riikonen-silc-spec-01.nroff
lib/silcclient/client.c
lib/silcclient/client.h
lib/silcclient/command.c
lib/silcclient/command_reply.c

diff --git a/CHANGES b/CHANGES
index 12028fe866ea54fef156eb322e49219dc8b615ae..db4eb1551f616cee31d51a40dfa85496e59c9cf2 100644 (file)
--- a/CHANGES
+++ b/CHANGES
@@ -1,3 +1,38 @@
+Sat Jan 27 22:34:56 EET 2001  Pekka Riikonen <priikone@poseidon.pspt.fi>
+
+       * silc_server_remove_channel_user checks now also global list
+         for channel and client.
+
+       * silc_server_new_channel_user checks now both local and global
+         list for channel and client.  Fixed a bug in client id decoding.
+         Used to decode wrong buffer.
+
+       * silc_server_channel_message checks now both local and global
+         list for channel entry.
+
+       * Tested channel joining (hence JOIN) in router environment
+         successfully.  Tested with two routers, two servers and two
+         clients.
+
+       * Tested channel message sending in router environment successfully.
+
+Thu Jan 11 03:22:57 EET 2001  Pekka Riikonen <priikone@poseidon.pspt.fi>
+
+       * Added silc_server_save_channel_key into server.[ch] to save the
+         received channel key in Channel Key payload processing. It is
+         also used in JOIN command reply handling.
+
+         Equivalent function silc_client_save_channel_key added into
+         client.[ch] into client library.
+
+       * Changed JOIN command reply to send information whether the channel
+         was created or not (is existing already) and the channel key 
+         payload.  Changed protocol specs accordingly.
+
+       * Fixed bugs in WHOIS and IDENTIFY command reply sending when
+         the request was sent by ID and not by nickname.  Crashed on
+         NULL dereference.
+
 Sat Dec 23 21:55:07 EET 2000  Pekka Riikonen <priikone@poseidon.pspt.fi>
 
        * Fixed a bug in Client library.  IDENTIFY and WHOIS reply functions
index c951889e53f0e37377bf1678c14fba17ef040a36..87eebd88d208b0c19f1714db6ab7c62d226ed6d7 100644 (file)
@@ -670,9 +670,17 @@ silc_server_command_whois_from_server(SilcServerCommandContext cmd)
 
   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));
+    if (!client_id) {
+      silc_server_command_send_status_data(cmd, SILC_COMMAND_WHOIS,
+                                          SILC_STATUS_ERR_NO_SUCH_NICK,
+                                          3, nick, strlen(nick));
+    } else {
+      SilcBuffer idp = silc_id_payload_encode(client_id, SILC_ID_CLIENT);
+      silc_server_command_send_status_data(cmd, SILC_COMMAND_WHOIS,
+                                          SILC_STATUS_ERR_NO_SUCH_CLIENT_ID,
+                                          2, idp->data, idp->len);
+      silc_buffer_free(idp);
+    }
     goto out;
   }
 
@@ -999,9 +1007,17 @@ silc_server_command_identify_from_client(SilcServerCommandContext cmd)
   
   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));
+    if (!client_id) {
+      silc_server_command_send_status_data(cmd, SILC_COMMAND_IDENTIFY,
+                                          SILC_STATUS_ERR_NO_SUCH_NICK,
+                                          3, nick, strlen(nick));
+    } else {
+      SilcBuffer idp = silc_id_payload_encode(client_id, SILC_ID_CLIENT);
+      silc_server_command_send_status_data(cmd, SILC_COMMAND_IDENTIFY,
+                                          SILC_STATUS_ERR_NO_SUCH_CLIENT_ID,
+                                          2, idp->data, idp->len);
+      silc_buffer_free(idp);
+    }
     goto out;
   }
 
@@ -1706,16 +1722,18 @@ SILC_SERVER_CMD_FUNC(add_to_channel)
 static void silc_server_command_join_channel(SilcServer server, 
                                             SilcServerCommandContext cmd,
                                             SilcChannelEntry channel,
+                                            SilcClientID *client_id,
                                             int created,
                                             unsigned int umode)
 {
   SilcSocketConnection sock = cmd->sock;
   unsigned char *tmp;
   unsigned int tmp_len;
-  unsigned char *passphrase = NULL, mode[4];
+  unsigned char *passphrase = NULL, mode[4], tmp2[4];
   SilcClientEntry client;
   SilcChannelClientEntry chl;
-  SilcBuffer reply, chidp, clidp;
+  SilcBuffer reply, chidp, clidp, keyp;
+  unsigned short ident = silc_command_get_ident(cmd->payload);
 
   SILC_LOG_DEBUG(("Start"));
 
@@ -1723,7 +1741,7 @@ static void silc_server_command_join_channel(SilcServer server,
     return;
 
   /* Get passphrase */
-  tmp = silc_argument_get_arg_type(cmd->args, 2, &tmp_len);
+  tmp = silc_argument_get_arg_type(cmd->args, 3, &tmp_len);
   if (tmp) {
     passphrase = silc_calloc(tmp_len, sizeof(*passphrase));
     memcpy(passphrase, tmp, tmp_len);
@@ -1773,21 +1791,20 @@ static void silc_server_command_join_channel(SilcServer server,
    * Client is allowed to join to the channel. Make it happen.
    */
 
-  /* 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) {
-    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, NULL);
+  /* Get the client entry */
+  if (cmd->sock->type == SILC_SOCKET_TYPE_CLIENT) {
+    client = (SilcClientEntry)sock->user_data;
+  } else {
+    client = silc_idlist_find_client_by_id(server->local_list, client_id, 
+                                          NULL);
     if (!client) {
-      /* XXX */
-      SILC_LOG_ERROR(("Forwarded join command did not find the client who "
-                     "wanted to join the channel"));
-      goto out;
+      /* XXX actually this is useless since router finds always cell's
+        local clients from its local lists. */
+      client = silc_idlist_find_client_by_id(server->global_list, client_id, 
+                                            NULL);
+      if (!client)
+       goto out;
     }
-    silc_free(id);
-  } else {
-    client = (SilcClientEntry)sock->user_data;
   }
 
   /* Check whether the client already is on the channel */
@@ -1798,9 +1815,15 @@ static void silc_server_command_join_channel(SilcServer server,
   }
 
   /* Generate new channel key as protocol dictates */
-  if (!created)
+  if (!created || !channel->channel_key)
     silc_server_create_channel_key(server, channel, 0);
 
+  /* Send the channel key. This is broadcasted to the channel but is not
+     sent to the client who is joining to the channel. */
+  silc_server_send_channel_key(server, channel, 
+                              server->server_type == SILC_ROUTER ? 
+                              FALSE : server->standalone);
+
   /* Join the client to the channel by adding it to channel's user list.
      Add also the channel to client entry's channels list for fast cross-
      referencing. */
@@ -1817,85 +1840,74 @@ static void silc_server_command_join_channel(SilcServer server,
   /* Encode command reply packet */
   chidp = silc_id_payload_encode(channel->id, SILC_ID_CHANNEL);
   SILC_PUT32_MSB(channel->mode, mode);
+  SILC_PUT32_MSB(created, tmp2);
+  tmp = silc_id_id2str(channel->id, SILC_ID_CHANNEL);
+  keyp = silc_channel_key_payload_encode(SILC_ID_CHANNEL_LEN, tmp, 
+                                        SILC_ID_CHANNEL_LEN,
+                                        channel->channel_key->cipher->name,
+                                        channel->key_len / 8, channel->key);
+  silc_free(tmp);
   if (!channel->topic) {
     reply = 
       silc_command_reply_payload_encode_va(SILC_COMMAND_JOIN,
-                                          SILC_STATUS_OK, 0, 3,
+                                          SILC_STATUS_OK, ident, 5,
                                           2, channel->channel_name,
                                           strlen(channel->channel_name),
                                           3, chidp->data, chidp->len,
-                                          4, mode, 4);
+                                          4, mode, 4,
+                                          5, tmp2, 4,
+                                          6, keyp->data, keyp->len);
   } else {
     reply = 
       silc_command_reply_payload_encode_va(SILC_COMMAND_JOIN,
-                                          SILC_STATUS_OK, 0, 4
+                                          SILC_STATUS_OK, ident, 6
                                           2, channel->channel_name, 
                                           strlen(channel->channel_name),
                                           3, chidp->data, chidp->len,
                                           4, mode, 4,
-                                          5, channel->topic, 
+                                          5, tmp2, 4,
+                                          6, keyp->data, keyp->len,
+                                          8, 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 */
+
+  /* Send command reply */
+  silc_server_packet_send(server, sock, SILC_PACKET_COMMAND_REPLY, 0, 
+                         reply->data, reply->len, FALSE);
+
+  if (!cmd->pending)
+    /* Send JOIN notify to locally connected clients on the channel */
     silc_server_send_notify_to_channel(server, channel, FALSE,
                                       SILC_NOTIFY_TYPE_JOIN, 1,
                                       clidp->data, clidp->len);
 
-    /* Broadcast NEW_CHANNEL_USER packet to primary route */
+  /* Send NEW_CHANNEL_USER packet to our primary router */
+  if (!cmd->pending && !server->standalone)
     silc_server_send_new_channel_user(server, server->router->connection,
-                                     TRUE, channel->id, SILC_ID_CHANNEL_LEN,
+                                     server->server_type == SILC_SERVER ?
+                                     FALSE : TRUE,
+                                     channel->id, SILC_ID_CHANNEL_LEN,
                                      client->id, SILC_ID_CLIENT_LEN);
 
-    silc_free(tmpid);
-  } else {
-    /* Client sent the command. Send all replies directly to the client. */
+  /* 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);
 
-    /* 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);
-    
-    /* 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);
+    FAQ:
 
-    /* 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);
+   * Kuinka NAMES komento händlätään serverissä kun router lähettää sen
+   serverille joka on lähettäny sille clientin puolesta JOIN komennon?
+   
+   R: Serverin pitää ymmärtää NAMES comman replyjä.
 
-    /* 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);
-  }
+  */
 
   silc_buffer_free(reply);
   silc_buffer_free(clidp);
   silc_buffer_free(chidp);
+  silc_buffer_free(keyp);
 
  out:
   if (passphrase)
@@ -1914,8 +1926,9 @@ SILC_SERVER_CMD_FUNC(join)
   SilcChannelEntry channel;
   unsigned int umode = 0;
   int created = FALSE;
+  SilcClientID *client_id;
 
-  SILC_SERVER_COMMAND_CHECK_ARGC(SILC_COMMAND_JOIN, cmd, 1, 3);
+  SILC_SERVER_COMMAND_CHECK_ARGC(SILC_COMMAND_JOIN, cmd, 1, 4);
 
   /* Get channel name */
   tmp = silc_argument_get_arg_type(cmd->args, 1, &tmp_len);
@@ -1933,48 +1946,96 @@ SILC_SERVER_CMD_FUNC(join)
     goto out;
   }
 
+  /* Get Client ID of the client who is joining to the channel */
+  tmp = silc_argument_get_arg_type(cmd->args, 2, &tmp_len);
+  if (!tmp) {
+    silc_server_command_send_status_reply(cmd, SILC_COMMAND_JOIN,
+                                         SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
+    goto out;
+  }
+  client_id = silc_id_payload_parse_id(tmp, tmp_len);
+
   /* Get cipher name */
-  cipher = silc_argument_get_arg_type(cmd->args, 3, NULL);
+  cipher = silc_argument_get_arg_type(cmd->args, 4, NULL);
 
   /* See if the channel exists */
   channel = silc_idlist_find_channel_by_name(server->local_list, 
                                             channel_name, NULL);
-  if (!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 {
 
-      /* 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;
+  if (cmd->sock->type == SILC_SOCKET_TYPE_CLIENT) {
+    /* If this is coming from client the Client ID in the command packet must
+       be same as the client's ID. */
+    if (cmd->sock->type == SILC_SOCKET_TYPE_CLIENT) {
+      SilcClientEntry entry = (SilcClientEntry)cmd->sock->user_data;
+      if (SILC_ID_CLIENT_COMPARE(entry->id, client_id)) {
+       silc_server_command_send_status_reply(cmd, SILC_COMMAND_JOIN,
+                                       SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
+       goto out;
       }
+    }
+
+    if (!channel) {
+      /* Channel not found */
+
+      /* If we are standalone server we don't have a router, we just create 
+        the channel by ourselves. */
+      if (server->standalone) {
+       channel = silc_server_create_new_channel(server, server->id, cipher, 
+                                                channel_name);
+       umode = (SILC_CHANNEL_UMODE_CHANOP | SILC_CHANNEL_UMODE_CHANFO);
+       created = TRUE;
+
+      } else {
+
+       /* The channel does not exist on our server. If we are normal server 
+          we will send JOIN command to our router which will handle the
+          joining procedure (either creates the channel if it doesn't exist 
+          or joins the client to it). */
+       if (server->server_type == SILC_SERVER) {
+         SilcBuffer tmpbuf;
+         unsigned short old_ident;
+         
+         old_ident = silc_command_get_ident(cmd->payload);
+         silc_command_set_ident(cmd->payload, silc_rng_get_rn16(server->rng));
+         tmpbuf = silc_command_payload_encode_payload(cmd->payload);
+         
+         /* Send JOIN command to our router */
+         silc_server_packet_send(server, (SilcSocketConnection)
+                                 server->router->connection,
+                                 SILC_PACKET_COMMAND, cmd->packet->flags,
+                                 tmpbuf->data, tmpbuf->len, TRUE);
+         
+         /* Reprocess this packet after received reply from router */
+         silc_server_command_pending(server, SILC_COMMAND_JOIN, 
+                                     silc_command_get_ident(cmd->payload),
+                                     silc_server_command_join, context);
+         cmd->pending = TRUE;
+         return;
+       }
+       
+       /* We are router and the channel does not seem exist so we will check
+          our global list as well for the channel. */
+       channel = silc_idlist_find_channel_by_name(server->global_list, 
+                                                  channel_name, NULL);
+       if (!channel) {
+         /* Channel really does not exist, create it */
+         channel = silc_server_create_new_channel(server, server->id, cipher, 
+                                                  channel_name);
+         umode = (SILC_CHANNEL_UMODE_CHANOP | SILC_CHANNEL_UMODE_CHANFO);
+         created = TRUE;
+       }
+      }
+    }
+  } else {
+    if (!channel) {
+      /* Channel not found */
+
+      /* If the command came from router and/or we are normal server then
+        something went wrong with the joining as the channel was not found.
+        We can't do anything else but ignore this. */
+      if (cmd->sock->type == SILC_SOCKET_TYPE_ROUTER ||
+         server->server_type == SILC_SERVER)
+       goto out;
       
       /* We are router and the channel does not seem exist so we will check
         our global list as well for the channel. */
@@ -1984,15 +2045,25 @@ SILC_SERVER_CMD_FUNC(join)
        /* 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;
+       umode = (SILC_CHANNEL_UMODE_CHANOP | SILC_CHANNEL_UMODE_CHANFO);
        created = TRUE;
       }
     }
   }
 
+  /* If the channel does not have global users and is also empty it means the
+     channel was created globally (by our router) and the client will be the
+     channel founder and operator. */
+  if (!channel->global_users && silc_list_count(channel->user_list) == 0) {
+    umode = (SILC_CHANNEL_UMODE_CHANOP | SILC_CHANNEL_UMODE_CHANFO);
+    created = TRUE;            /* Created globally by our router */
+  }
+
   /* Join to the channel */
-  silc_server_command_join_channel(server, cmd, channel, created, umode);
+  silc_server_command_join_channel(server, cmd, channel, client_id,
+                                  created, umode);
+
+  silc_free(client_id);
 
  out:
   silc_server_command_free(cmd);
index ff9556d1233e7eb607755a4c0430e0e35625df93..d248f83e21a88881dd08bdf1c135b983e5916fa6 100644 (file)
@@ -96,7 +96,7 @@ void silc_server_command_reply_process(SilcServer server,
     if (cmd->cmd == command)
       break;
 
-  if (cmd == NULL) {
+  if (cmd == NULL || !cmd->cb) {
     silc_free(ctx);
     return;
   }
@@ -402,14 +402,16 @@ SILC_SERVER_CMD_REPLY_FUNC(join)
   unsigned int len;
   unsigned char *id_string;
   char *channel_name, *tmp;
+  unsigned int mode, created;
+  SilcBuffer keyp;
 
   SILC_LOG_DEBUG(("Start"));
 
   COMMAND_CHECK_STATUS;
 
   /* Get channel name */
-  tmp = silc_argument_get_arg_type(cmd->args, 2, NULL);
-  if (!tmp)
+  channel_name = silc_argument_get_arg_type(cmd->args, 2, NULL);
+  if (!channel_name)
     goto out;
 
   /* Get channel ID */
@@ -417,28 +419,66 @@ SILC_SERVER_CMD_REPLY_FUNC(join)
   if (!id_string)
     goto out;
 
-  channel_name = strdup(tmp);
-  id = silc_id_payload_parse_id(id_string, len);
+  /* Get mode mask */
+  tmp = silc_argument_get_arg_type(cmd->args, 4, NULL);
+  if (!tmp)
+    goto out;
+  SILC_GET32_MSB(mode, tmp);
 
-  /* XXX We should check that we have sent JOIN command to the router
-     in the first place. Also should check that we don't have the channel
-     already in the cache. These checks must be made because of possible
-     buggy routers. */
+  /* Get created boolean value */
+  tmp = silc_argument_get_arg_type(cmd->args, 5, NULL);
+  if (!tmp)
+    goto out;
+  SILC_GET32_MSB(created, tmp);
 
-  SILC_LOG_DEBUG(("Adding new channel %s id(%s)", channel_name,
-                 silc_id_render(id, SILC_ID_CHANNEL)));
+  /* Get channel key */
+  tmp = silc_argument_get_arg_type(cmd->args, 6, &len);
+  if (!tmp)
+    goto out;
+  keyp = silc_buffer_alloc(len);
+  silc_buffer_pull_tail(keyp, SILC_BUFFER_END(keyp));
+  silc_buffer_put(keyp, tmp, len);
 
-  /* Add the channel to our local list. */
-  entry = silc_idlist_add_channel(server->local_list, channel_name, 
-                                 SILC_CHANNEL_MODE_NONE, id, 
-                                 server->router, NULL);
+  id = silc_id_payload_parse_id(id_string, len);
+
+  /* See whether we already have the channel. */
+  entry = silc_idlist_find_channel_by_id(server->local_list, id, NULL);
   if (!entry) {
-    silc_free(channel_name);
+    /* Add new channel */
+
+    SILC_LOG_DEBUG(("Adding new [%s] channel %s id(%s)", 
+                   (created == 0 ? "created" : "existing"), channel_name,
+                   silc_id_render(id, SILC_ID_CHANNEL)));
+
+    /* Add the channel to our local list. */
+    entry = silc_idlist_add_channel(server->local_list, strdup(channel_name), 
+                                   SILC_CHANNEL_MODE_NONE, id, 
+                                   server->router, NULL);
+    if (!entry) {
+      silc_free(id);
+      goto out;
+    }
+  } else {
     silc_free(id);
-    goto out;
   }
 
-  //entry->global_users = TRUE;
+  /* If channel was not created we know there is global users on the 
+     channel. */
+  entry->global_users = (created == 0 ? TRUE : FALSE);
+
+  /* If channel was just created the mask must be zero */
+  if (!entry->global_users && mode) {
+    SILC_LOG_DEBUG(("Buggy router `%s' sent non-zero mode mask for "
+                   "new channel, forcing it to zero", cmd->sock->hostname));
+    mode = 0;
+  }
+
+  /* Save channel mode */
+  entry->mode = mode;
+
+  /* Save channel key */
+  silc_server_save_channel_key(server, keyp, entry);
+  silc_buffer_free(keyp);
 
   /* Execute any pending commands */
   SILC_SERVER_COMMAND_EXEC_PENDING(cmd, SILC_COMMAND_JOIN);
index 507c73022f25c950f48b1700c7a0ddbc2b5ea487..cd276491d30fdd9ba1ab98efc127e8e80cb1f966 100644 (file)
@@ -344,6 +344,8 @@ silc_idlist_find_client_by_nickname(SilcIDList id_list, char *nickname,
       *ret_entry = id_cache;
   }
 
+  SILC_LOG_DEBUG(("Found"));
+
   return client;
 }
 
@@ -389,6 +391,8 @@ silc_idlist_find_client_by_hash(SilcIDList id_list, char *nickname,
   if (ret_entry)
     *ret_entry = id_cache;
 
+  SILC_LOG_DEBUG(("Found"));
+
   return client;
 }
 
@@ -415,6 +419,8 @@ silc_idlist_find_client_by_id(SilcIDList id_list, SilcClientID *id,
   if (ret_entry)
     *ret_entry = id_cache;
 
+  SILC_LOG_DEBUG(("Found"));
+
   return client;
 }
 
@@ -552,6 +558,8 @@ silc_idlist_find_channel_by_name(SilcIDList id_list, char *name,
 
   silc_idcache_list_free(list);
 
+  SILC_LOG_DEBUG(("Found"));
+
   return channel;
 }
 
@@ -578,5 +586,7 @@ silc_idlist_find_channel_by_id(SilcIDList id_list, SilcChannelID *id,
   if (ret_entry)
     *ret_entry = id_cache;
 
+  SILC_LOG_DEBUG(("Found"));
+
   return channel;
 }
index 5cac18a4bc1695a4bd851544e07a3b245e662a79..e3a491d4d6d48b649aae6371a261feb45f26cb77 100644 (file)
@@ -212,7 +212,6 @@ void silc_server_channel_message(SilcServer server,
 
   /* Sanity checks */
   if (packet->dst_id_type != SILC_ID_CHANNEL) {
-    SILC_LOG_ERROR(("Received bad message for channel, dropped"));
     SILC_LOG_DEBUG(("Received bad message for channel, dropped"));
     goto out;
   }
@@ -221,8 +220,11 @@ void silc_server_channel_message(SilcServer server,
   id = silc_id_str2id(packet->dst_id, SILC_ID_CHANNEL);
   channel = silc_idlist_find_channel_by_id(server->local_list, id, NULL);
   if (!channel) {
-    SILC_LOG_DEBUG(("Could not find channel"));
-    goto out;
+    channel = silc_idlist_find_channel_by_id(server->global_list, id, NULL);
+    if (!channel) {
+      SILC_LOG_DEBUG(("Could not find channel"));
+      goto out;
+    }
   }
 
   /* See that this client is on the channel. If the message is coming
@@ -263,74 +265,19 @@ void silc_server_channel_key(SilcServer server,
                             SilcPacketContext *packet)
 {
   SilcBuffer buffer = packet->buffer;
-  SilcChannelKeyPayload payload = NULL;
-  SilcChannelID *id = NULL;
   SilcChannelEntry channel;
-  SilcChannelClientEntry chl;
-  unsigned char *tmp;
-  unsigned int tmp_len;
-  char *cipher;
-  int exist = FALSE;
 
   if (packet->src_id_type != SILC_ID_SERVER)
-    goto out;
-
-  /* Decode channel key payload */
-  payload = silc_channel_key_payload_parse(buffer);
-  if (!payload) {
-    SILC_LOG_ERROR(("Bad channel key payload, dropped"));
-    goto out;
-  }
-
-  /* Get channel ID */
-  tmp = silc_channel_key_get_id(payload, &tmp_len);
-  id = silc_id_payload_parse_id(tmp, tmp_len);
-  if (!id)
-    goto out;
-
-  /* Get the channel entry */
-  channel = silc_idlist_find_channel_by_id(server->local_list, id, NULL);
-  if (!channel) {
-    SILC_LOG_ERROR(("Received key for non-existent channel"));
-    goto out;
-  }
-
-  tmp = silc_channel_key_get_key(payload, &tmp_len);
-  if (!tmp)
-    goto out;
-
-  cipher = silc_channel_key_get_cipher(payload, NULL);;
-  if (!cipher)
-    goto out;
-
-  /* Remove old key if exists */
-  if (channel->key) {
-    memset(channel->key, 0, channel->key_len / 8);
-    silc_free(channel->key);
-    silc_cipher_free(channel->channel_key);
-    exist = TRUE;
-  }
-
-  /* Create new cipher */
-  if (!silc_cipher_alloc(cipher, &channel->channel_key))
-    goto out;
+    return;
 
-  /* Save the key */
-  channel->key_len = tmp_len * 8;
-  channel->key = silc_calloc(tmp_len, sizeof(unsigned char));
-  memcpy(channel->key, tmp, tmp_len);
-  channel->channel_key->cipher->set_key(channel->channel_key->context, 
-                                       tmp, tmp_len);
+  /* Save the channel key */
+  channel = silc_server_save_channel_key(server, buffer, NULL);
+  if (!channel)
+    return;
 
   /* Distribute the key to everybody who is on the channel. If we are router
      we will also send it to locally connected servers. */
   silc_server_send_channel_key(server, channel, FALSE);
-
- out:
-  if (id)
-    silc_free(id);
-  if (payload)
-    silc_channel_key_payload_free(payload);
 }
 
 /* Received packet to replace a ID. This checks that the requested ID
@@ -390,12 +337,20 @@ void silc_server_replace_id(SilcServer server,
   /* Replace the old ID */
   switch(old_id_type) {
   case SILC_ID_CLIENT:
+    SILC_LOG_DEBUG(("Old Client ID id(%s)", 
+                   silc_id_render(id, SILC_ID_CLIENT)));
+    SILC_LOG_DEBUG(("New Client ID id(%s)", 
+                   silc_id_render(id2, SILC_ID_CLIENT)));
     if (silc_idlist_replace_client_id(server->local_list, id, id2) == NULL)
       if (server->server_type == SILC_ROUTER)
        silc_idlist_replace_client_id(server->global_list, id, id2);
     break;
 
   case SILC_ID_SERVER:
+    SILC_LOG_DEBUG(("Old Server ID id(%s)", 
+                   silc_id_render(id, SILC_ID_CLIENT)));
+    SILC_LOG_DEBUG(("New Server ID id(%s)", 
+                   silc_id_render(id2, SILC_ID_CLIENT)));
     if (silc_idlist_replace_server_id(server->local_list, id, id2) == NULL)
       if (server->server_type == SILC_ROUTER)
        silc_idlist_replace_server_id(server->global_list, id, id2);
@@ -792,18 +747,24 @@ void silc_server_remove_channel_user(SilcServer server,
                            buffer->data, buffer->len, FALSE);
   }
 
-  /* XXX routers should check server->global_list as well */
   /* Get channel entry */
   channel = silc_idlist_find_channel_by_id(server->local_list, 
                                           channel_id, NULL);
-  if (!channel)
-    goto out;
-  
-  /* XXX routers should check server->global_list as well */
+  if (!channel) {
+    channel = silc_idlist_find_channel_by_id(server->global_list, 
+                                            channel_id, NULL);
+    if (!channel)
+      goto out;
+  }
+
   /* Get client entry */
   client = silc_idlist_find_client_by_id(server->local_list, client_id, NULL);
-  if (!client)
-    goto out;
+  if (!client) {
+    client = silc_idlist_find_client_by_id(server->global_list, 
+                                          client_id, NULL);
+    if (!client)
+      goto out;
+  }
 
   /* Remove from channel */
   silc_server_remove_from_one_channel(server, sock, channel, client, FALSE);
@@ -919,6 +880,8 @@ void silc_server_notify(SilcServer server,
       goto out;
     }
 
+    channel->global_users = TRUE;
+
     /* Send to channel */
     silc_server_packet_send_to_channel(server, channel, packet->type, FALSE,
                                       packet->buffer->data, 
@@ -995,10 +958,11 @@ void silc_server_new_channel_user(SilcServer server,
     goto out;
 
   /* Decode the client ID */
-  client_id = silc_id_str2id(tmpid1, SILC_ID_CLIENT);
+  client_id = silc_id_str2id(tmpid2, SILC_ID_CLIENT);
   if (!client_id)
     goto out;
 
+#if 0
   /* If the packet is originated from the one who sent it to us we know
      that the ID belongs to our cell, unless the sender was router. */
   tmpid = silc_id_str2id(packet->src_id, SILC_ID_SERVER);
@@ -1015,12 +979,19 @@ void silc_server_new_channel_user(SilcServer server,
     router = server->router;
   }
   silc_free(tmpid);
+#endif
+
+  router_sock = sock;
+  router = sock->user_data;
 
   /* Find the channel */
-  channel = silc_idlist_find_channel_by_id(id_list, channel_id, NULL);
+  channel = silc_idlist_find_channel_by_id(server->local_list, 
+                                          channel_id, NULL);
   if (!channel) {
-    SILC_LOG_ERROR(("Received channel user for non-existent channel"));
-    goto out;
+    channel = silc_idlist_find_channel_by_id(server->global_list, 
+                                            channel_id, NULL);
+    if (!channel)
+      goto out;
   }
 
   /* If we are router and this packet is not already broadcast packet
@@ -1033,12 +1004,13 @@ void silc_server_new_channel_user(SilcServer server,
                            packet->buffer->data, packet->buffer->len, FALSE);
   }
 
+  SILC_LOG_DEBUG(("Client ID: %s", silc_id_render(client_id, SILC_ID_CLIENT)));
+
   /* Get client entry */
-  client = silc_idlist_find_client_by_id(id_list, client_id, NULL);
+  client = silc_idlist_find_client_by_id(server->local_list, client_id, NULL);
   if (!client) {
-    /* This is new client to us, add entry to ID list */
-    client = silc_idlist_add_client(id_list, NULL, NULL, NULL, 
-                                   client_id, router, router_sock);
+    client = silc_idlist_find_client_by_id(server->global_list, 
+                                          client_id, NULL);
     if (!client)
       goto out;
   }
index 41d5c22cf193cb251d0237041180ef2477212032..7c529233bf1a30eaa27eb4067214e4e2bdae2138 100644 (file)
@@ -441,6 +441,9 @@ void silc_server_packet_send_to_channel(SilcServer server,
       continue;
     }
 
+    if (server->server_type == SILC_ROUTER && !route)
+      continue;
+
     /* Send to locally connected client */
     if (client) {
 
@@ -1088,14 +1091,14 @@ void silc_server_send_new_channel_user(SilcServer server,
 
   SILC_LOG_DEBUG(("Start"));
 
-  clid = silc_id_id2str(client_id, SILC_ID_CLIENT);
-  if (!clid)
-    return;
-
   chid = silc_id_id2str(channel_id, SILC_ID_CHANNEL);
   if (!chid)
     return;
 
+  clid = silc_id_id2str(client_id, SILC_ID_CLIENT);
+  if (!clid)
+    return;
+
   packet = silc_buffer_alloc(2 + 2 + channel_id_len + client_id_len);
   silc_buffer_pull_tail(packet, SILC_BUFFER_END(packet));
   silc_buffer_format(packet,
@@ -1115,10 +1118,10 @@ void silc_server_send_new_channel_user(SilcServer server,
 
 /* Send Channel Key payload to distribute the new channel key. Normal server
    sends this to router when new client joins to existing channel. Router
-   sends this to the local server who forwarded join command in case where
-   the channel did not exist yet.  Both normal and router servers uses this
+   sends this to the local server who sent the join command in case where
+   the channel did not exist yet. Both normal and router servers uses this
    also to send this to locally connected clients on the channel. This
-   must not be broadcasted packet. */
+   must not be broadcasted packet. Routers do not send this to each other. */
 
 void silc_server_send_channel_key(SilcServer server,
                                  SilcChannelEntry channel,
index 1d97ab1ac2690ffb941e7fc18d617851ebff33ff..1aabffbdb444ca5e38855f2f111c97c5841d6aa2 100644 (file)
@@ -1884,7 +1884,7 @@ void silc_server_create_channel_key(SilcServer server,
   unsigned int len;
 
   if (!channel->channel_key)
-    return;
+    silc_cipher_alloc("twofish", &channel->channel_key);
 
   if (key_len)
     len = key_len;
@@ -1912,3 +1912,84 @@ void silc_server_create_channel_key(SilcServer server,
   memcpy(channel->key, channel_key, len);
   memset(channel_key, 0, sizeof(channel_key));
 }
+
+/* Saves the channel key found in the encoded `key_payload' buffer. This 
+   function is used when we receive Channel Key Payload and also when we're
+   processing JOIN command reply. Returns entry to the channel. */
+
+SilcChannelEntry silc_server_save_channel_key(SilcServer server,
+                                             SilcBuffer key_payload,
+                                             SilcChannelEntry channel)
+{
+  SilcChannelKeyPayload payload = NULL;
+  SilcChannelID *id = NULL;
+  unsigned char *tmp;
+  unsigned int tmp_len;
+  char *cipher;
+
+  /* Decode channel key payload */
+  payload = silc_channel_key_payload_parse(key_payload);
+  if (!payload) {
+    SILC_LOG_ERROR(("Bad channel key payload, dropped"));
+    channel = NULL;
+    goto out;
+  }
+
+  /* Get the channel entry */
+  if (!channel) {
+
+    /* Get channel ID */
+    tmp = silc_channel_key_get_id(payload, &tmp_len);
+    id = silc_id_str2id(tmp, SILC_ID_CHANNEL);
+    if (!id) {
+      channel = NULL;
+      goto out;
+    }
+
+    channel = silc_idlist_find_channel_by_id(server->local_list, id, NULL);
+    if (!channel) {
+      SILC_LOG_ERROR(("Received key for non-existent channel"));
+      goto out;
+    }
+  }
+
+  tmp = silc_channel_key_get_key(payload, &tmp_len);
+  if (!tmp) {
+    channel = NULL;
+    goto out;
+  }
+
+  cipher = silc_channel_key_get_cipher(payload, NULL);;
+  if (!cipher) {
+    channel = NULL;
+    goto out;
+  }
+
+  /* Remove old key if exists */
+  if (channel->key) {
+    memset(channel->key, 0, channel->key_len / 8);
+    silc_free(channel->key);
+    silc_cipher_free(channel->channel_key);
+  }
+
+  /* Create new cipher */
+  if (!silc_cipher_alloc(cipher, &channel->channel_key)) {
+    channel = NULL;
+    goto out;
+  }
+
+  /* Save the key */
+  channel->key_len = tmp_len * 8;
+  channel->key = silc_calloc(tmp_len, sizeof(unsigned char));
+  memcpy(channel->key, tmp, tmp_len);
+  channel->channel_key->cipher->set_key(channel->channel_key->context, 
+                                       tmp, tmp_len);
+
+ out:
+  if (id)
+    silc_free(id);
+  if (payload)
+    silc_channel_key_payload_free(payload);
+
+  return channel;
+}
index 7d38a7e71aeeb68152845f5fc867e22e329c9d03..52624050e6551061cec424fa10e9b885d038edbc 100644 (file)
@@ -109,5 +109,8 @@ SilcChannelEntry silc_server_create_new_channel(SilcServer server,
 void silc_server_create_channel_key(SilcServer server, 
                                    SilcChannelEntry channel,
                                    unsigned int key_len);
+SilcChannelEntry silc_server_save_channel_key(SilcServer server,
+                                             SilcBuffer key_payload,
+                                             SilcChannelEntry channel);
 
 #endif
index 3499f165d240478fc0630f4cdd2cb85bd8c7cbe5..5810c45b66bf66bc0aa64c0103d4fbe46c5f9f1a 100644 (file)
@@ -1340,6 +1340,10 @@ payload with the session key shared between the server and
 the client.  After that, client starts using the key received
 in this payload to protect the traffic on the channel.
 
+The client who is joining to the channel receives its key in the
+SILC_COMMAND_JOIN command reply message thus it is not necessary to
+send this payload to the entity who sent the SILC_COMMAND_JOIN command.
+
 Channel keys are cell specific thus every router in cell have
 to create a channel key and distribute it if any client in the
 cell has joined to a channel.  Channel traffic between cell's
index 9e6e731cde3c7fd50f6ffb64043387c30d242115..1064e9db6cbe08a93eb472939894644ce4e24ecf 100644 (file)
@@ -1442,9 +1442,8 @@ new client joins to channel, whether the channel has existed previously
 or not.  This way the new client on the channel is not able to decrypt
 any of the old traffic on the channel.  Client who receives the reply to
 the join command must start using the received Channel ID in the channel
-message communication thereafter.  However, client must not start
-communicating on the channel before it has received the packet
-SILC_PACKET_CHANNEL_KEY.
+message communication thereafter.  Client also receives the key for the
+channel in the command reply.
 
 If client wants to know the other clients currently on the channel
 the client must send SILC_COMMAND_NAMES command to receive a list of
@@ -1779,6 +1778,7 @@ List of all defined commands in SILC follows.
             SILC_STATUS_LIST_START
             SILC_STATUS_LIST_END
             SILC_STATUS_ERR_NO_SUCH_NICK
+            SILC_STATUS_ERR_NO_SUCH_CLIENT_ID
             SILC_STATUS_ERR_WILDCARDS
             SILC_STATUS_ERR_NOT_ENOUGH_PARAMS
             SILC_STATUS_ERR_TOO_MANY_PARAMS
@@ -1894,6 +1894,7 @@ List of all defined commands in SILC follows.
             SILC_STATUS_LIST_START
             SILC_STATUS_LIST_END
             SILC_STATUS_ERR_NO_SUCH_NICK
+            SILC_STATUS_ERR_NO_SUCH_CLIENT_ID
             SILC_STATUS_ERR_WILDCARDS
             SILC_STATUS_ERR_NOT_ENOUGH_PARAMS
             SILC_STATUS_ERR_TOO_MANY_PARAMS
@@ -2227,9 +2228,9 @@ List of all defined commands in SILC follows.
 
    14   SILC_COMMAND_JOIN
 
-        Max Arguments:  3
-            Arguments:  (1) <channel>  (2) [<passphrase>] 
-                        (3) [<cipher>]
+        Max Arguments:  4
+            Arguments:  (1) <channel>       (2) <Client ID>
+                        (3) [<passphrase>]  (4) [<cipher>]
 
         Join to channel/create new channel.  This command is used to
         join to a channel.  If the channel does not exist the channel is
@@ -2241,6 +2242,10 @@ List of all defined commands in SILC follows.
         The name of the <channel> must not include any spaces (` '),
         non-printable characters, commas (`,') or any wildcard characters.
 
+        The second argument <Client ID> is the Client ID of the client who
+        is joining to the client.  When client sends this command to the
+        server the <Client ID> must be the client's own ID.
+
         Cipher to be used to secure the traffic on the channel may be
         requested by sending the name of the requested <cipher>.  This
         is used only if the channel does not exist and is created.  If
@@ -2265,11 +2270,12 @@ List of all defined commands in SILC follows.
 
         Reply messages to the command:
 
-        Max Arguments:  6
+        Max Arguments:  9
             Arguments:  (1) <Status Payload>  (2) <channel> 
                         (3) <Channel ID>      (4) <channel mode mask>
-                        (5) [<ban mask>]      (6) [<invite list>]
-                        (6) [<topic>]
+                        (5) <created>         (6) <Channel Key Payload>
+                        (7) [<ban mask>]      (8) [<invite list>]
+                        (9) [<topic>]
 
         This command replies with the channel name requested by the
         client, channel ID of the channel and topic of the channel
@@ -2278,10 +2284,8 @@ List of all defined commands in SILC follows.
         channel is created the mode mask is zero (0).  If ban mask
         and/or invite list is set they are sent as well.
 
-        Client must not start transmitting to the channel even after
-        server has replied to this command.  Client is permitted to 
-        start transmitting on channel after server has sent packet 
-        SILC_PACKET_CHANNEL_KEY to the client.
+        Client receives the channel key in the reply message as well
+        inside <Channel Key Payload>.
 
         Status messages:
 
index 53aec139a2b6b07eb85da6806657b2d020f265da..eaf4db454aad1a40604ea88f23dc66b6f3dd545b 100644 (file)
@@ -1781,27 +1781,21 @@ void silc_client_new_channel_id(SilcClient client,
                   (void *)channel->id, (void *)channel, TRUE);
 }
 
-/* Processes received key for channel. The received key will be used
-   to protect the traffic on the channel for now on. Client must receive
-   the key to the channel before talking on the channel is possible. 
-   This is the key that server has generated, this is not the channel
-   private key, it is entirely local setting. */
+/* Saves channel key from encoded `key_payload'. This is used when we
+   receive Channel Key Payload and when we are processing JOIN command 
+   reply. */
 
-void silc_client_receive_channel_key(SilcClient client,
-                                    SilcSocketConnection sock,
-                                    SilcBuffer packet)
+void silc_client_save_channel_key(SilcClientConnection conn,
+                                 SilcBuffer key_payload, 
+                                 SilcChannelEntry channel)
 {
   unsigned char *id_string, *key, *cipher;
   unsigned int tmp_len;
-  SilcClientConnection conn = (SilcClientConnection)sock->user_data;
   SilcChannelID *id;
   SilcIDCacheEntry id_cache = NULL;
-  SilcChannelEntry channel;
   SilcChannelKeyPayload payload;
 
-  SILC_LOG_DEBUG(("Received key for channel"));
-  
-  payload = silc_channel_key_payload_parse(packet);
+  payload = silc_channel_key_payload_parse(key_payload);
   if (!payload)
     return;
 
@@ -1814,21 +1808,24 @@ void silc_client_receive_channel_key(SilcClient client,
   id = silc_id_str2id(id_string, SILC_ID_CHANNEL);
 
   /* Find channel. */
-  if (!silc_idcache_find_by_id_one(conn->channel_cache, (void *)id,
-                                  SILC_ID_CHANNEL, &id_cache))
-    goto out;
+  if (!channel) {
+    if (!silc_idcache_find_by_id_one(conn->channel_cache, (void *)id,
+                                    SILC_ID_CHANNEL, &id_cache))
+      goto out;
     
+    /* Get channel entry */
+    channel = (SilcChannelEntry)id_cache->context;
+  }
+
   /* Save the key */
   key = silc_channel_key_get_key(payload, &tmp_len);
   cipher = silc_channel_key_get_cipher(payload, NULL);
-
-  channel = (SilcChannelEntry)id_cache->context;
   channel->key_len = tmp_len;
   channel->key = silc_calloc(tmp_len, sizeof(*channel->key));
   memcpy(channel->key, key, tmp_len);
 
   if (!silc_cipher_alloc(cipher, &channel->channel_key)) {
-    client->ops->say(client, conn,
+    conn->client->ops->say(conn->client, conn,
                     "Cannot talk to channel: unsupported cipher %s", cipher);
     goto out;
   }
@@ -1843,6 +1840,22 @@ void silc_client_receive_channel_key(SilcClient client,
   silc_channel_key_payload_free(payload);
 }
 
+/* Processes received key for channel. The received key will be used
+   to protect the traffic on the channel for now on. Client must receive
+   the key to the channel before talking on the channel is possible. 
+   This is the key that server has generated, this is not the channel
+   private key, it is entirely local setting. */
+
+void silc_client_receive_channel_key(SilcClient client,
+                                    SilcSocketConnection sock,
+                                    SilcBuffer packet)
+{
+  SILC_LOG_DEBUG(("Received key for channel"));
+
+  /* Save the key */
+  silc_client_save_channel_key(sock->user_data, packet, NULL);
+}
+
 /* Process received message to a channel (or from a channel, really). This
    decrypts the channel message with channel specific key and parses the
    channel payload. Finally it displays the message on the screen. */
index fdff610eee9e0ae4bcafa828d3540eeeafe0e81a..f6d2215af736de0a5ed75e41304d172fb653c164 100644 (file)
@@ -272,6 +272,9 @@ void silc_client_new_channel_id(SilcClient client,
                                SilcSocketConnection sock,
                                char *channel_name,
                                unsigned int mode, SilcIDPayload idp);
+void silc_client_save_channel_key(SilcClientConnection conn,
+                                 SilcBuffer key_payload, 
+                                 SilcChannelEntry channel);
 void silc_client_receive_channel_key(SilcClient client,
                                     SilcSocketConnection sock,
                                     SilcBuffer packet);
index 4dd9a23e8bda4b9bc6f6e771406a31e6e51ab1bd..29eceb036b15b17c6ff628f4dcfa5c362c5f8a27 100644 (file)
@@ -635,7 +635,7 @@ SILC_CLIENT_CMD_FUNC(join)
   SilcClientCommandContext cmd = (SilcClientCommandContext)context;
   SilcClientConnection conn = cmd->conn;
   SilcIDCacheEntry id_cache = NULL;
-  SilcBuffer buffer;
+  SilcBuffer buffer, idp;
 
   if (!cmd->conn) {
     SILC_NOT_CONNECTED(cmd->client, cmd->conn);
@@ -662,27 +662,33 @@ SILC_CLIENT_CMD_FUNC(join)
     goto out;
   }
 
+  idp = silc_id_payload_encode(conn->local_id, SILC_ID_CLIENT);
+
   /* Send JOIN command to the server */
-  if (cmd->argc == 2)
+  if (cmd->argc == 3)
     buffer = 
-      silc_command_payload_encode_va(SILC_COMMAND_JOIN, 0, 1,
-                                    1, cmd->argv[1], cmd->argv_lens[1]);
-  else if (cmd->argc == 3)
+      silc_command_payload_encode_va(SILC_COMMAND_JOIN, 0, 2,
+                                    1, cmd->argv[1], cmd->argv_lens[1],
+                                    2, idp->data, idp->len);
+  else if (cmd->argc == 4)
     /* XXX Buggy */
     buffer = 
-      silc_command_payload_encode_va(SILC_COMMAND_JOIN, 0, 2,
+      silc_command_payload_encode_va(SILC_COMMAND_JOIN, 0, 3,
                                     1, cmd->argv[1], cmd->argv_lens[1],
-                                    2, cmd->argv[2], cmd->argv_lens[2]);
+                                    2, idp->data, idp->len,
+                                    3, cmd->argv[2], cmd->argv_lens[2]);
   else
     buffer = 
-      silc_command_payload_encode_va(SILC_COMMAND_JOIN, 0, 3,
+      silc_command_payload_encode_va(SILC_COMMAND_JOIN, 0, 4,
                                     1, cmd->argv[1], cmd->argv_lens[1],
-                                    2, cmd->argv[2], cmd->argv_lens[2],
-                                    3, cmd->argv[3], cmd->argv_lens[3]);
+                                    2, idp->data, idp->len,
+                                    3, cmd->argv[2], cmd->argv_lens[2],
+                                    4, cmd->argv[3], cmd->argv_lens[3]);
 
   silc_client_packet_send(cmd->client, conn->sock, SILC_PACKET_COMMAND, NULL, 
                          0, NULL, NULL, buffer->data, buffer->len, TRUE);
   silc_buffer_free(buffer);
+  silc_buffer_free(idp);
 
   /* Notify application */
   COMMAND;
index d901385b48325eb34016e8b2304ecafacb40cc6f..4570a027310978a5d49ce192a19dc84b2615bc26 100644 (file)
@@ -692,9 +692,10 @@ SILC_CLIENT_CMD_REPLY_FUNC(join)
   SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
   SilcClient client = cmd->client;
   SilcCommandStatus status;
-  SilcIDPayload idp;
+  SilcIDPayload idp = NULL;
   unsigned int argc, mode, len;
-  char *topic, *tmp, *channel_name;
+  char *topic, *tmp, *channel_name = NULL;
+  SilcBuffer keyp;
 
   SILC_LOG_DEBUG(("Start"));
 
@@ -707,7 +708,7 @@ SILC_CLIENT_CMD_REPLY_FUNC(join)
   }
 
   argc = silc_argument_get_arg_num(cmd->args);
-  if (argc < 3 || argc > 5) {
+  if (argc < 3 || argc > 9) {
     cmd->client->ops->say(cmd->client, conn,
             "Cannot join channel: Bad reply packet");
     COMMAND_REPLY_ERROR;
@@ -730,6 +731,7 @@ SILC_CLIENT_CMD_REPLY_FUNC(join)
     cmd->client->ops->say(cmd->client, conn, 
                          "Cannot join channel: Bad reply packet");
     COMMAND_REPLY_ERROR;
+    silc_free(channel_name);
     goto out;
   }
   idp = silc_id_payload_parse_data(tmp, len);
@@ -741,14 +743,29 @@ SILC_CLIENT_CMD_REPLY_FUNC(join)
   else
     mode = 0;
 
+  /* Get channel key */
+  tmp = silc_argument_get_arg_type(cmd->args, 6, &len);
+  if (!tmp) {
+    silc_id_payload_free(idp);
+    silc_free(channel_name);
+    goto out;
+  }
+  keyp = silc_buffer_alloc(len);
+  silc_buffer_pull_tail(keyp, SILC_BUFFER_END(keyp));
+  silc_buffer_put(keyp, tmp, len);
+
   /* Get topic */
-  topic = silc_argument_get_arg_type(cmd->args, 5, NULL);
+  topic = silc_argument_get_arg_type(cmd->args, 8, NULL);
 
   /* Save received Channel ID */
   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);
+
   if (topic)
     client->ops->say(cmd->client, conn, 
                     "Topic for %s: %s", channel_name, topic);