updates.
authorPekka Riikonen <priikone@silcnet.org>
Sun, 11 Mar 2001 16:57:58 +0000 (16:57 +0000)
committerPekka Riikonen <priikone@silcnet.org>
Sun, 11 Mar 2001 16:57:58 +0000 (16:57 +0000)
19 files changed:
CHANGES
TODO
apps/silc/client_ops.c
apps/silc/silc.c
apps/silcd/command.c
apps/silcd/command_reply.c
apps/silcd/packet_receive.c
apps/silcd/server.c
apps/silcd/server.h
doc/draft-riikonen-silc-spec-01.nroff
lib/silcclient/client.c
lib/silcclient/client_channel.c
lib/silcclient/client_keyagr.c
lib/silcclient/command_reply.c
lib/silcclient/idlist.c
lib/silcclient/idlist.h
lib/silcclient/protocol.c
lib/silcclient/protocol.h
lib/silcclient/silcapi.h

diff --git a/CHANGES b/CHANGES
index 5b1a5015f65ee7c6c8b71e9ec6ffb529c7121887..9870d7c467fec317733533de5530af0bdf35126b 100644 (file)
--- a/CHANGES
+++ b/CHANGES
@@ -1,3 +1,70 @@
+Sun Mar 11 14:59:05 EET 2001  Pekka Riikonen <priikone@poseidon.pspt.fi>
+
+       * Added silc_client_get_clients_by_list to get client entries
+         from Client ID list, that is returned for example by JOIN
+         and USERS command replies.  The application should use this
+         function for example when JOIN command reply is received to
+         resolve the clients already on the channel (library does not
+         do that anymore as USERS command reply is not used in the JOIN
+         procedure anymore).  Affected files lib/silcclient/silcapi.h and
+         lib/silcclient/idlist.c.
+
+       * JOIN command reply and USERS command reply returns now SilcBuffer
+         pointers instead of unsigned char pointers when returning
+         the client list and mode list.
+
+       * Added <Client ID> argument to the JOIN command reply, mainly
+         for the server to identify for which client the command was
+         originally sent.  Updated protocol specs accordingly.
+
+       * Added SilcDlist private_key pointer to the SilcChannelEntry
+         in the client to support the channel private keys.  Affected
+         file is lib/silcclient/idlist.h.
+
+       * Added SilcChannelPrivateKey argument to the function
+         silc_client_send_channel_message so that application can choose
+         to use specific private ke if it wants to.  If it is not provided,
+         the normal channel key is used, unless private keys are set. 
+         In this case the first (key that was added first) is used 
+         as the encryption key.
+
+       * Implemented the support for channel private key handling.
+         Implemented the following functions:
+
+         silc_client_add_channel_private_key,
+         silc_client_del_channel_private_keys,
+         silc_client_del_channel_private_key,
+         silc_client_list_channel_private_keys and
+         silc_client_free_channel_private_keys
+
+         Affected file lib/silcclient/client_channel.c.
+
+       * Added the support for the private keys in the channel message
+         sending and encryption and in the message reception and
+         decryption.  Affected funtions are
+         silc_client_send_channel_message and silc_client_channel_message.
+
+Sat Mar 10 21:36:22 EET 2001  Pekka Riikonen <priikone@poseidon.pspt.fi>
+
+       * Added SKE's key verify callback to the client library's
+         KE protocol context. Affected files lib/silcclient/protocol.[ch].
+
+       * Removed the statement that server (or router) must send USERS
+         command reply when joining to the channel so that the client
+         knows who are on the channel.  Instead, the client list and 
+         client's mode list is now sent in the JOIN command reply to the
+         client who joined channel.  This is better solution.
+
+       * Added function silc_server_get_users_on_channel and function
+         silc_server_save_users_on_channel to the silcd/server.[ch].
+
+       * Removed function silc_server_command_send_users from the
+         silcd/command.c.
+
+       * Do not show topic on the client library anymore.  The topic is
+         sent in the command reply notify to the application and the
+         application must show the topic now.
+
 Sat Mar 10 00:07:37 EET 2001  Pekka Riikonen <priikone@poseidon.pspt.fi>
 
        * Added client searching by nickname hash into the IDENTIFY and
diff --git a/TODO b/TODO
index c408e032b7919882300ba914753396e9ebe70f74..201673dc13edd44ad58f0a30d26a6f631b52c47c 100644 (file)
--- a/TODO
+++ b/TODO
@@ -62,6 +62,12 @@ New features TODO
 TODO In SILC Client Library
 ===========================
 
+ o Add client library parameters or options that handle what kind of
+   messages the library should print out (using `say' client operation,
+   for example) and what is left for the application to print.  The
+   appliation could for example set that it handles all command printing
+   but all error printing should be handled by the library, etc...
+
  o Non-blocking connection on the background must be stopped if some
    other connection on same window has established.  Now it is possible
    that some non-blocking connection timeouts on the background when
index acf3186a237fbc8fdda1d116d03c22a61777b4da..5524529b5a9d3ba6c07980dd8efb2a1babcd88df 100644 (file)
@@ -294,6 +294,53 @@ void silc_command(SilcClient client, SilcClientConnection conn,
     }
 }
 
+/* We've resolved all clients we don't know about, now just print the
+   users from the channel on the screen. */
+
+void silc_client_show_users(SilcClient client,
+                           SilcClientConnection conn,
+                           SilcClientEntry *clients,
+                           unsigned int clients_count,
+                           void *context)
+{
+  SilcChannelEntry channel = (SilcChannelEntry)context;
+  SilcChannelUser chu;
+  int k = 0, len1 = 0, len2 = 0;
+  char *name_list = NULL;
+
+  if (!clients)
+    return;
+
+  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++;
+  }
+  
+  client->ops->say(client, conn, "Users on %s: %s", channel->channel_name, 
+                  name_list);
+  silc_free(name_list);
+}
+
 /* Command reply handler. This function is called always in the command reply
    function. If error occurs it will be called as well. Normal scenario
    is that it will be called after the received command data has been parsed
@@ -329,12 +376,34 @@ void silc_command_reply(SilcClient client, SilcClientConnection conn,
     case SILC_COMMAND_JOIN:
       {
        unsigned int mode;
+       char *topic;
+       SilcBuffer client_id_list;
+       unsigned int list_count;
+       SilcChannelEntry channel;
 
        app->screen->bottom_line->channel = va_arg(vp, char *);
-       (void)va_arg(vp, void *);
+       channel = va_arg(vp, SilcChannelEntry);
        mode = va_arg(vp, unsigned int);
+       (void)va_arg(vp, unsigned int);
+       (void)va_arg(vp, unsigned char *);
+       (void)va_arg(vp, unsigned char *);
+       (void)va_arg(vp, unsigned char *);
+       topic = va_arg(vp, char *);
+       (void)va_arg(vp, unsigned char *);
+       list_count = va_arg(vp, unsigned int);
+       client_id_list = va_arg(vp, SilcBuffer);
+
+       if (topic)
+         client->ops->say(client, conn, "Topic for %s: %s", 
+                          app->screen->bottom_line->channel, topic);
+       
        app->screen->bottom_line->channel_mode = silc_client_chmode(mode);
        silc_screen_print_bottom_line(app->screen, 0);
+
+       /* Resolve the client information */
+       silc_client_get_clients_by_list(client, conn, list_count,
+                                       client_id_list,
+                                       silc_client_show_users, channel);
       }
       break;
 
@@ -628,7 +697,6 @@ int silc_get_auth_method(SilcClient client, SilcClientConnection conn,
 void silc_failure(SilcClient client, SilcClientConnection conn, 
                  SilcProtocol protocol, void *failure)
 {
-  SilcClientInternal app = (SilcClientInternal)client->application;
 
 }
 
index bfb4ab72b9fad180a9145e34c22153c999cc2cec..ff7e28dc7240b99fc8f00e9c565218e2e2a8995c 100644 (file)
@@ -566,7 +566,7 @@ static void silc_client_process_message(SilcClientInternal app)
       silc_print(app->client, "> %s", data);
       silc_client_send_channel_message(app->client, 
                                       app->conn,
-                                      app->conn->current_channel,
+                                      app->conn->current_channel, NULL,
                                       data, strlen(data), TRUE);
     }
   }
index 1a89aae0147e054091a2743897bb1c77902327c4..2850f39d353758cfb6d50c4e740b6750b2c32082 100644 (file)
@@ -37,10 +37,6 @@ silc_server_command_send_status_data(SilcServerCommandContext cmd,
                                     unsigned int arg_type,
                                     unsigned char *arg,
                                     unsigned int arg_len);
-void silc_server_command_send_users(SilcServer server,
-                                   SilcSocketConnection sock,
-                                   SilcChannelEntry channel,
-                                   int pending);
 
 /* Server command list. */
 SilcServerCommand silc_command_list[] =
@@ -1627,6 +1623,9 @@ SILC_SERVER_CMD_FUNC(quit)
 
   SILC_SERVER_COMMAND_CHECK_ARGC(SILC_COMMAND_QUIT, cmd, 0, 1);
 
+  if (cmd->sock->type != SILC_SOCKET_TYPE_CLIENT)
+    goto out;
+
   /* Get destination ID */
   tmp = silc_argument_get_arg_type(cmd->args, 1, &len);
   if (len > 128)
@@ -1642,6 +1641,7 @@ SILC_SERVER_CMD_FUNC(quit)
                     silc_server_command_quit_cb, (void *)q,
                     0, 200000, SILC_TASK_TIMEOUT, SILC_TASK_PRI_LOW);
 
+ out:
   silc_server_command_free(cmd);
 }
 
@@ -1747,66 +1747,6 @@ SILC_SERVER_CMD_FUNC(ping)
   silc_server_command_free(cmd);
 }
 
-/* Assembles USERS command and executes it. This is called when client
-   joins to a channel and we wan't to send USERS command reply to the 
-   client. */
-
-void silc_server_command_send_users(SilcServer server,
-                                   SilcSocketConnection sock,
-                                   SilcChannelEntry channel,
-                                   int pending)
-{
-  SilcServerCommandContext cmd;
-  SilcBuffer buffer, idp;
-  SilcPacketContext *packet = silc_packet_context_alloc();
-
-  SILC_LOG_DEBUG(("Start"));
-
-  /* Create USERS command packet and process it. */
-  idp = silc_id_payload_encode(channel->id, SILC_ID_CHANNEL);
-  buffer = silc_command_payload_encode_va(SILC_COMMAND_USERS, 0, 1,
-                                         1, idp->data, idp->len);
-
-  packet->buffer = silc_buffer_copy(buffer);
-  packet->sock = sock;
-  packet->type = SILC_PACKET_COMMAND;
-
-  cmd = silc_server_command_alloc();
-  cmd->payload = silc_command_payload_parse(buffer);
-  if (!cmd->payload) {
-    silc_free(cmd);
-    silc_buffer_free(buffer);
-    silc_buffer_free(idp);
-    silc_packet_context_free(packet);
-    return;
-  }
-  cmd->args = silc_command_get_args(cmd->payload);
-  cmd->server = server;
-  cmd->sock = silc_socket_dup(sock);
-  cmd->packet = silc_packet_context_dup(packet);
-  cmd->pending = FALSE;
-
-  if (pending) {
-    /* If this function was called from pending command then instead of
-       processing the command now, register a pending command callback which
-       will process it after we've received the automatic USERS command 
-       reply which server will send in JOIN. */
-    silc_server_command_pending(server, SILC_COMMAND_USERS, 0, NULL,
-                               silc_server_command_users, cmd);
-    cmd->pending = TRUE;
-    silc_buffer_free(buffer);
-    silc_buffer_free(idp);
-    return;
-  }
-
-  /* Process USERS command. */
-  silc_server_command_users((void *)cmd);
-
-  silc_buffer_free(buffer);
-  silc_buffer_free(idp);
-  silc_packet_context_free(packet);
-}
-
 /* Internal routine to join channel. The channel sent to this function
    has been either created or resolved from ID lists. This joins the sent
    client to the channel. */
@@ -1820,11 +1760,11 @@ static void silc_server_command_join_channel(SilcServer server,
 {
   SilcSocketConnection sock = cmd->sock;
   unsigned char *tmp;
-  unsigned int tmp_len;
-  unsigned char *passphrase = NULL, mode[4], tmp2[4];
+  unsigned int tmp_len, user_count;
+  unsigned char *passphrase = NULL, mode[4], tmp2[4], tmp3[4];
   SilcClientEntry client;
   SilcChannelClientEntry chl;
-  SilcBuffer reply, chidp, clidp, keyp;
+  SilcBuffer reply, chidp, clidp, keyp, user_list, mode_list;
   unsigned short ident = silc_command_get_ident(cmd->payload);
 
   SILC_LOG_DEBUG(("Start"));
@@ -1927,6 +1867,10 @@ static void silc_server_command_join_channel(SilcServer server,
   silc_list_add(channel->user_list, chl);
   silc_list_add(client->channels, chl);
 
+  /* Get users on the channel */
+  silc_server_get_users_on_channel(server, channel, &user_list, &mode_list,
+                                  &user_count);
+
   /* Encode Client ID Payload of the original client who wants to join */
   clidp = silc_id_payload_encode(client->id, SILC_ID_CLIENT);
 
@@ -1934,6 +1878,7 @@ static void silc_server_command_join_channel(SilcServer server,
   chidp = silc_id_payload_encode(channel->id, SILC_ID_CHANNEL);
   SILC_PUT32_MSB(channel->mode, mode);
   SILC_PUT32_MSB(created, tmp2);
+  SILC_PUT32_MSB(user_count, tmp3);
   tmp = silc_id_id2str(channel->id, SILC_ID_CHANNEL);
   keyp = silc_channel_key_payload_encode(SILC_ID_CHANNEL_LEN, tmp, 
                                         strlen(channel->channel_key->
@@ -1944,25 +1889,35 @@ static void silc_server_command_join_channel(SilcServer server,
   if (!channel->topic) {
     reply = 
       silc_command_reply_payload_encode_va(SILC_COMMAND_JOIN,
-                                          SILC_STATUS_OK, ident, 5,
+                                          SILC_STATUS_OK, ident, 9,
                                           2, channel->channel_name,
                                           strlen(channel->channel_name),
                                           3, chidp->data, chidp->len,
-                                          4, mode, 4,
-                                          5, tmp2, 4,
-                                          6, keyp->data, keyp->len);
+                                          4, clidp->data, clidp->len,
+                                          5, mode, 4,
+                                          6, tmp2, 4,
+                                          7, keyp->data, keyp->len,
+                                          12, tmp3, 4,
+                                          13, user_list->data, user_list->len,
+                                          14, mode_list->data, 
+                                          mode_list->len);
   } else {
     reply = 
       silc_command_reply_payload_encode_va(SILC_COMMAND_JOIN,
-                                          SILC_STATUS_OK, ident, 6
+                                          SILC_STATUS_OK, ident, 10
                                           2, channel->channel_name, 
                                           strlen(channel->channel_name),
                                           3, chidp->data, chidp->len,
-                                          4, mode, 4,
-                                          5, tmp2, 4,
-                                          6, keyp->data, keyp->len,
-                                          9, channel->topic, 
-                                          strlen(channel->topic));
+                                          4, clidp->data, clidp->len,
+                                          5, mode, 4,
+                                          6, tmp2, 4,
+                                          7, keyp->data, keyp->len,
+                                          10, channel->topic, 
+                                          strlen(channel->topic),
+                                          12, tmp3, 4,
+                                          13, user_list->data, user_list->len,
+                                          14, mode_list->data, 
+                                          mode_list->len);
   }
 
   /* Send command reply */
@@ -1984,14 +1939,12 @@ static void silc_server_command_join_channel(SilcServer server,
                                   SILC_ID_CLIENT_LEN);
   }
 
-  /* Send USERS command reply to the joined channel so the user sees who
-     is currently on the channel. */
-  silc_server_command_send_users(server, sock, channel, cmd->pending);
-
   silc_buffer_free(reply);
   silc_buffer_free(clidp);
   silc_buffer_free(chidp);
   silc_buffer_free(keyp);
+  silc_buffer_free(user_list);
+  silc_buffer_free(mode_list);
 
  out:
   if (passphrase)
@@ -3255,14 +3208,12 @@ SILC_SERVER_CMD_FUNC(users)
   SilcServerCommandContext cmd = (SilcServerCommandContext)context;
   SilcServer server = cmd->server;
   SilcChannelEntry channel;
-  SilcChannelClientEntry chl;
   SilcChannelID *id;
   SilcBuffer packet;
   unsigned char *channel_id;
   unsigned int channel_id_len;
   SilcBuffer client_id_list;
   SilcBuffer client_mode_list;
-  SilcBuffer idp;
   unsigned char lc[4];
   unsigned int list_count = 0;
   unsigned short ident = silc_command_get_ident(cmd->payload);
@@ -3324,33 +3275,9 @@ SILC_SERVER_CMD_FUNC(users)
     }
   }
 
-  /* Assemble the lists now */
-
-  client_id_list = silc_buffer_alloc((SILC_ID_CLIENT_LEN + 4) * 
-                                    silc_list_count(channel->user_list));
-  silc_buffer_pull_tail(client_id_list, SILC_BUFFER_END(client_id_list));
-  client_mode_list = 
-    silc_buffer_alloc(4 * silc_list_count(channel->user_list));
-  silc_buffer_pull_tail(client_mode_list, SILC_BUFFER_END(client_mode_list));
-
-  silc_list_start(channel->user_list);
-  while ((chl = silc_list_get(channel->user_list)) != SILC_LIST_END) {
-    /* Client ID */
-    idp = silc_id_payload_encode(chl->client->id, SILC_ID_CLIENT);
-    silc_buffer_put(client_id_list, idp->data, idp->len);
-    silc_buffer_pull(client_id_list, idp->len);
-    silc_buffer_free(idp);
-
-    /* Client's mode on channel */
-    SILC_PUT32_MSB(chl->mode, client_mode_list->data);
-    silc_buffer_pull(client_mode_list, 4);
-
-    list_count++;
-  }
-  silc_buffer_push(client_id_list, 
-                  client_id_list->data - client_id_list->head);
-  silc_buffer_push(client_mode_list, 
-                  client_mode_list->data - client_mode_list->head);
+  /* Get the users list */
+  silc_server_get_users_on_channel(server, channel, &client_id_list,
+                                  &client_mode_list, &list_count);
 
   /* List count */
   SILC_PUT32_MSB(list_count, lc);
index 60e9f9f76c1b9fe0ca41843ec219f63c35172611..360b85d2ae378320d40925ba0b461c443d92f205 100644 (file)
@@ -375,13 +375,14 @@ SILC_SERVER_CMD_REPLY_FUNC(join)
   SilcServer server = cmd->server;
   SilcCommandStatus status;
   SilcChannelID *id;
+  SilcClientID *client_id = NULL;
   SilcChannelEntry entry;
   SilcHmac hmac = NULL;
-  unsigned int id_len, len;
+  unsigned int id_len, len, list_count;
   unsigned char *id_string;
   char *channel_name, *tmp;
   unsigned int mode, created;
-  SilcBuffer keyp;
+  SilcBuffer keyp, client_id_list, client_mode_list;
 
   COMMAND_CHECK_STATUS;
 
@@ -395,14 +396,22 @@ SILC_SERVER_CMD_REPLY_FUNC(join)
   if (!id_string)
     goto out;
 
+  /* Get client ID */
+  tmp = silc_argument_get_arg_type(cmd->args, 4, &len);
+  if (!tmp)
+    goto out;
+  client_id = silc_id_payload_parse_id(tmp, len);
+  if (!client_id)
+    goto out;
+
   /* Get mode mask */
-  tmp = silc_argument_get_arg_type(cmd->args, 4, NULL);
+  tmp = silc_argument_get_arg_type(cmd->args, 5, NULL);
   if (!tmp)
     goto out;
   SILC_GET32_MSB(mode, tmp);
 
   /* Get created boolean value */
-  tmp = silc_argument_get_arg_type(cmd->args, 5, NULL);
+  tmp = silc_argument_get_arg_type(cmd->args, 6, NULL);
   if (!tmp)
     goto out;
   SILC_GET32_MSB(created, tmp);
@@ -410,7 +419,7 @@ SILC_SERVER_CMD_REPLY_FUNC(join)
     goto out;
 
   /* 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)
     goto out;
   keyp = silc_buffer_alloc(len);
@@ -422,12 +431,36 @@ SILC_SERVER_CMD_REPLY_FUNC(join)
     goto out;
 
   /* Get hmac */
-  tmp = silc_argument_get_arg_type(cmd->args, 10, NULL);
+  tmp = silc_argument_get_arg_type(cmd->args, 11, NULL);
   if (tmp) {
     if (!silc_hmac_alloc(tmp, NULL, &hmac))
       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);
+
   /* See whether we already have the channel. */
   entry = silc_idlist_find_channel_by_id(server->local_list, id, NULL);
   if (!entry) {
@@ -467,11 +500,21 @@ SILC_SERVER_CMD_REPLY_FUNC(join)
   silc_server_save_channel_key(server, keyp, entry);
   silc_buffer_free(keyp);
 
+  /* Save the users to the channel */
+  silc_server_save_users_on_channel(server, cmd->sock, entry, 
+                                   client_id, client_id_list,
+                                   client_mode_list, list_count);
+
+  silc_buffer_free(client_id_list);
+  silc_buffer_free(client_mode_list);
+
   /* Execute any pending commands */
   SILC_SERVER_PENDING_EXEC(cmd, SILC_COMMAND_JOIN);
 
  out:
   SILC_SERVER_PENDING_DESTRUCTOR(cmd, SILC_COMMAND_JOIN);
+  if (client_id)
+    silc_free(client_id);
   silc_server_command_reply_free(cmd);
 }
 
@@ -486,7 +529,7 @@ SILC_SERVER_CMD_REPLY_FUNC(users)
   SilcBuffer client_mode_list;
   unsigned char *tmp;
   unsigned int tmp_len;
-  unsigned int list_count, i;
+  unsigned int list_count;
 
   COMMAND_CHECK_STATUS;
 
@@ -498,6 +541,16 @@ SILC_SERVER_CMD_REPLY_FUNC(users)
   if (!channel_id)
     goto out;
 
+  /* Get channel entry */
+  channel = silc_idlist_find_channel_by_id(server->local_list, 
+                                          channel_id, NULL);
+  if (!channel) {
+    channel = silc_idlist_find_channel_by_id(server->global_list, 
+                                            channel_id, NULL);
+    if (!channel)
+      goto out;
+  }
+
   /* Get the list count */
   tmp = silc_argument_get_arg_type(cmd->args, 3, &tmp_len);
   if (!tmp)
@@ -522,74 +575,10 @@ SILC_SERVER_CMD_REPLY_FUNC(users)
   silc_buffer_pull_tail(client_mode_list, tmp_len);
   silc_buffer_put(client_mode_list, tmp, tmp_len);
 
-  /* Get channel entry */
-  channel = silc_idlist_find_channel_by_id(server->local_list, 
-                                          channel_id, NULL);
-  if (!channel) {
-    channel = silc_idlist_find_channel_by_id(server->global_list, 
-                                            channel_id, NULL);
-    if (!channel)
-      goto out;
-  }
-
-  /* Cache the received Client ID's and modes. This cache expires
-     whenever server sends notify message to channel. It means two things;
-     some user has joined or leaved the channel. XXX! */
-  for (i = 0; i < list_count; i++) {
-    unsigned short idp_len;
-    unsigned int mode;
-    SilcClientID *client_id;
-    SilcClientEntry client;
-
-    /* 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;
-    silc_buffer_pull(client_id_list, idp_len);
-    
-    /* Mode */
-    SILC_GET32_MSB(mode, client_mode_list->data);
-    silc_buffer_pull(client_mode_list, 4);
-
-    /* Check if we have this client cached already. */
-    client = silc_idlist_find_client_by_id(server->local_list, client_id,
-                                          NULL);
-    if (!client)
-      client = silc_idlist_find_client_by_id(server->global_list, 
-                                            client_id, NULL);
-    if (!client) {
-      /* If router did not find such Client ID in its lists then this must
-        be bogus client or some router in the net is buggy. */
-      if (server->server_type == SILC_ROUTER)
-       goto out;
-
-      /* We don't have that client anywhere, add it. The client is added
-        to global list since server didn't have it in the lists so it must be 
-        global. */
-      client = silc_idlist_add_client(server->global_list, NULL, NULL, 
-                                     NULL, client_id, cmd->sock->user_data, 
-                                     NULL);
-      if (!client) {
-       silc_free(client_id);
-       continue;
-      }
-    } else {
-      /* We have the client already. */
-      silc_free(client_id);
-    }
-
-    if (!silc_server_client_on_channel(client, channel)) {
-      /* Client was not on the channel, add it. */
-      SilcChannelClientEntry chl = silc_calloc(1, sizeof(*chl));
-      chl->client = client;
-      chl->mode = mode;
-      chl->channel = channel;
-      silc_list_add(channel->user_list, chl);
-      silc_list_add(client->channels, chl);
-    }
-  }
+  /* Save the users to the channel */
+  silc_server_save_users_on_channel(server, cmd->sock, channel, NULL,
+                                   client_id_list, client_mode_list, 
+                                   list_count);
 
   silc_buffer_free(client_id_list);
   silc_buffer_free(client_mode_list);
index eb3f94556926063c5530ca9b6abbb6a6c0aeeb94..7c8df0e88cc85a411d31c0110e1aa932b0757dea 100644 (file)
@@ -174,8 +174,12 @@ void silc_server_notify(SilcServer server,
     channel = silc_idlist_find_channel_by_id(server->local_list, 
                                             channel_id, NULL);
     if (!channel) { 
-      silc_free(channel_id);
-      goto out;
+      channel = silc_idlist_find_channel_by_id(server->global_list, 
+                                              channel_id, NULL);
+      if (!channel) {
+       silc_free(channel_id);
+       goto out;
+      }
     }
 
     /* Get client ID */
index a7385384c09d9dffc6c3d7fb5be23b43d66d5a32..8fafd2428a6fab8213e43dccc0fff2d9a8bb51c0 100644 (file)
@@ -2000,7 +2000,7 @@ void silc_server_free_sock_user_data(SilcServer server,
 
       /* If this was our primary router connection then we're lost to
         the outside world. */
-      if (server->server_type == SILC_SERVER && server->router == user_data) {
+      if (server->router == user_data) {
        server->id_entry->router = NULL;
        server->router = NULL;
        server->standalone = TRUE;
@@ -2899,3 +2899,125 @@ SILC_TASK_CALLBACK(silc_server_failure_callback)
 
   silc_free(f);
 }
+
+/* Assembles user list and users mode list from the `channel'. */
+
+void silc_server_get_users_on_channel(SilcServer server,
+                                     SilcChannelEntry channel,
+                                     SilcBuffer *user_list,
+                                     SilcBuffer *mode_list,
+                                     unsigned int *user_count)
+{
+  SilcChannelClientEntry chl;
+  SilcBuffer client_id_list;
+  SilcBuffer client_mode_list;
+  SilcBuffer idp;
+  unsigned int list_count = 0;
+
+  client_id_list = silc_buffer_alloc((SILC_ID_CLIENT_LEN + 4) * 
+                                    silc_list_count(channel->user_list));
+  client_mode_list = silc_buffer_alloc(4 * 
+                                      silc_list_count(channel->user_list));
+  silc_buffer_pull_tail(client_id_list, SILC_BUFFER_END(client_id_list));
+  silc_buffer_pull_tail(client_mode_list, SILC_BUFFER_END(client_mode_list));
+  silc_list_start(channel->user_list);
+  while ((chl = silc_list_get(channel->user_list)) != SILC_LIST_END) {
+    /* Client ID */
+    idp = silc_id_payload_encode(chl->client->id, SILC_ID_CLIENT);
+    silc_buffer_put(client_id_list, idp->data, idp->len);
+    silc_buffer_pull(client_id_list, idp->len);
+    silc_buffer_free(idp);
+
+    /* Client's mode on channel */
+    SILC_PUT32_MSB(chl->mode, client_mode_list->data);
+    silc_buffer_pull(client_mode_list, 4);
+
+    list_count++;
+  }
+  silc_buffer_push(client_id_list, 
+                  client_id_list->data - client_id_list->head);
+  silc_buffer_push(client_mode_list, 
+                  client_mode_list->data - client_mode_list->head);
+
+  *user_list = client_id_list;
+  *mode_list = client_mode_list;
+  *user_count = list_count;
+}
+
+/* Saves users and their modes to the `channel'. */
+
+void silc_server_save_users_on_channel(SilcServer server,
+                                      SilcSocketConnection sock,
+                                      SilcChannelEntry channel,
+                                      SilcClientID *noadd,
+                                      SilcBuffer user_list,
+                                      SilcBuffer mode_list,
+                                      unsigned int user_count)
+{
+  int i;
+
+  /* Cache the received Client ID's and modes. This cache expires
+     whenever server sends notify message to channel. It means two things;
+     some user has joined or leaved the channel. XXX TODO! */
+  for (i = 0; i < user_count; i++) {
+    unsigned short idp_len;
+    unsigned int mode;
+    SilcClientID *client_id;
+    SilcClientEntry client;
+
+    /* Client ID */
+    SILC_GET16_MSB(idp_len, user_list->data + 2);
+    idp_len += 4;
+    client_id = silc_id_payload_parse_id(user_list->data, idp_len);
+    silc_buffer_pull(user_list, idp_len);
+    if (!client_id)
+      continue;
+
+    /* Mode */
+    SILC_GET32_MSB(mode, mode_list->data);
+    silc_buffer_pull(mode_list, 4);
+
+    if (noadd && !SILC_ID_CLIENT_COMPARE(client_id, noadd)) {
+      silc_free(client_id);
+      continue;
+    }
+    
+    /* Check if we have this client cached already. */
+    client = silc_idlist_find_client_by_id(server->local_list, client_id,
+                                          NULL);
+    if (!client)
+      client = silc_idlist_find_client_by_id(server->global_list, 
+                                            client_id, NULL);
+    if (!client) {
+      /* If router did not find such Client ID in its lists then this must
+        be bogus client or some router in the net is buggy. */
+      if (server->server_type == SILC_ROUTER) {
+       silc_free(client_id);
+       continue;
+      }
+
+      /* We don't have that client anywhere, add it. The client is added
+        to global list since server didn't have it in the lists so it must be 
+        global. */
+      client = silc_idlist_add_client(server->global_list, NULL, NULL, NULL, 
+                                     silc_id_dup(client_id, SILC_ID_CLIENT), 
+                                     sock->user_data, NULL);
+      if (!client) {
+       silc_free(client_id);
+       continue;
+      }
+    }
+
+    silc_free(client_id);
+
+    if (!silc_server_client_on_channel(client, channel)) {
+      /* Client was not on the channel, add it. */
+      SilcChannelClientEntry chl = silc_calloc(1, sizeof(*chl));
+      chl->client = client;
+      chl->mode = mode;
+      chl->channel = channel;
+      silc_list_add(channel->user_list, chl);
+      silc_list_add(client->channels, chl);
+    }
+  }
+}
index e8b31ca7857814ac9e20dde7f1dd7559d451cddd..ce1194cb025f17540a4245fd962e9fdd284a30bc 100644 (file)
@@ -141,5 +141,17 @@ void silc_server_perform_heartbeat(SilcSocketConnection sock,
 void silc_server_announce_servers(SilcServer server);
 void silc_server_announce_clients(SilcServer server);
 void silc_server_announce_channels(SilcServer server);
+void silc_server_get_users_on_channel(SilcServer server,
+                                     SilcChannelEntry channel,
+                                     SilcBuffer *user_list,
+                                     SilcBuffer *mode_list,
+                                     unsigned int *user_count);
+void silc_server_save_users_on_channel(SilcServer server,
+                                      SilcSocketConnection sock,
+                                      SilcChannelEntry channel,
+                                      SilcClientID *noadd,
+                                      SilcBuffer user_list,
+                                      SilcBuffer mode_list,
+                                      unsigned int user_count);
 
 #endif
index d8163389913a89502629b07d959c44c1e5c5a128..6032a360c745d61b2a4288f0034836f3c7995538 100644 (file)
@@ -1580,12 +1580,6 @@ send information about newly joined client to all routers in the SILC
 network.  This is done by broadcasting the SILC_NOTIFY_TYPE_JOIN notify
 type to the router's primary route. 
 
-After joining the client to the channel server or router must send
-command reply packet for SILC_COMMAND_USERS command.  This way the
-client gets the list of users on the channel.  If the router joined
-the client to the channel then the router sends this command reply
-to the server which must send it further to the original client.
-
 It is important to note that new channel key is created always when
 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
@@ -2433,20 +2427,27 @@ List of all defined commands in SILC follows.
 
         Reply messages to the command:
 
-        Max Arguments:  10
-            Arguments:  (1) <Status Payload>  (2) <channel> 
-                        (3) <Channel ID>      (4) <channel mode mask>
-                        (5) <created>         (6) <Channel Key Payload>
-                        (7) [<ban mask>]      (8) [<invite list>]
-                        (9) [<topic>]        (10) [<hmac>]
+        Max Arguments:  14
+            Arguments:  (1) <Status Payload>      (2) <channel> 
+                        (3) <Channel ID>          (4) <Client ID>
+                        (5) <channel mode mask>   (6) <created>
+                        (7) <Channel Key Payload> (8) [<ban mask>]
+                        (9) [<invite list>]       (10) [<topic>]
+                       (11) [<hmac>]              (12) <list count>
+                       (13) <Client ID list>      (14) <client mode list>
 
         This command replies with the channel name requested by the
         client, channel ID of the channel and topic of the channel
-        if it exists.  It also replies with the channel mode mask
+        if it exists.  The <Client ID> is the Client ID which was joined
+        to the channel.  It also replies with the channel mode mask
         which tells all the modes set on the channel.  If the
         channel is created the mode mask is zero (0).  If ban mask
         and/or invite list is set they are sent as well.
 
+        The <list count>, <Client ID list> and <client mode list> are
+        the clients curerntly on the channel and their modes on the
+        channel.
+
         Client receives the channel key in the reply message as well
         inside <Channel Key Payload>.
 
index 33d51e100bf8d48c6bc274157af95c390298738a..1e9d13e00420f51833ab1bd384bcda6f7514fadd 100644 (file)
@@ -276,6 +276,7 @@ int silc_client_start_key_exchange(SilcClient client,
   proto_ctx->rng = client->rng;
   proto_ctx->responder = FALSE;
   proto_ctx->send_packet = silc_client_protocol_ke_send_packet;
+  proto_ctx->verify = silc_client_protocol_ke_verify_key;
 
   /* Perform key exchange protocol. silc_client_connect_to_server_final
      will be called after the protocol is finished. */
index c12532ca6d68af0ca77977354cd045bf1b5b4afe..81c9ca9a5b2f33030fb97f2dcba444f1eafb4291 100644 (file)
@@ -35,6 +35,7 @@
 void silc_client_send_channel_message(SilcClient client, 
                                      SilcClientConnection conn,
                                      SilcChannelEntry channel,
+                                     SilcChannelPrivateKey key,
                                      unsigned char *data, 
                                      unsigned int data_len, 
                                      int force_send)
@@ -50,14 +51,40 @@ void silc_client_send_channel_message(SilcClient client,
 
   SILC_LOG_DEBUG(("Sending packet to channel"));
 
-  if (!channel || !channel->key || !channel->hmac) {
+  if (!channel || !channel->hmac || 
+      (!channel->channel_key && !key && !channel->private_keys)) {
     client->ops->say(client, conn, 
                     "Cannot talk to channel: key does not exist");
     return;
   }
 
+  /* Take the key to be used */
+  if (key) {
+    /* Use key application specified */
+    cipher = key->cipher;
+    hmac = key->hmac;
+  } else if (channel->curr_key) {
+    /* Use current private key */
+    cipher = channel->curr_key->cipher;
+    hmac = channel->curr_key->hmac;
+  } else if (!channel->curr_key && channel->private_keys) {
+    /* Use just some private key since we don't know what to use 
+       and private keys are set. */
+    silc_dlist_start(channel->private_keys);
+    key = silc_dlist_get(channel->private_keys);
+    cipher = key->cipher;
+    hmac = key->hmac;
+
+    /* Use this key as current private key */
+    channel->curr_key = key;
+  } else {
+    /* Use normal channel key generated by the server */
+    cipher = channel->channel_key;
+    hmac = channel->hmac;
+  }
+
   /* Generate IV */
-  iv_len = silc_cipher_get_block_len(channel->channel_key);
+  iv_len = silc_cipher_get_block_len(cipher);
   if (channel->iv[0] == '\0')
     for (i = 0; i < iv_len; i++) channel->iv[i] = 
                                   silc_rng_get_byte(client->rng);
@@ -66,8 +93,8 @@ void silc_client_send_channel_message(SilcClient client,
 
   /* Encode the channel payload. This also encrypts the message payload. */
   payload = silc_channel_payload_encode(data_len, data, iv_len, 
-                                       channel->iv, channel->channel_key,
-                                       channel->hmac, client->rng);
+                                       channel->iv, cipher, hmac,
+                                       client->rng);
 
   /* Get data used in packet header encryption, keys and stuff. */
   cipher = conn->send_key;
@@ -162,11 +189,29 @@ void silc_client_channel_message(SilcClient client,
 
   channel = (SilcChannelEntry)id_cache->context;
 
-  /* Parse the channel message payload. This also decrypts the payload */
-  payload = silc_channel_payload_parse(buffer, channel->channel_key,
-                                      channel->hmac);
-  if (!payload)
-    goto out;
+  /* If there is no channel private key then just decrypt the message 
+     with the channel key. If private keys are set then just go through
+     all private keys and check what decrypts correctly. */
+  if (!channel->private_keys) {
+    /* Parse the channel message payload. This also decrypts the payload */
+    payload = silc_channel_payload_parse(buffer, channel->channel_key,
+                                        channel->hmac);
+    if (!payload)
+      goto out;
+  } else {
+    SilcChannelPrivateKey entry;
+
+    silc_dlist_start(channel->private_keys);
+    while ((entry = silc_dlist_get(channel->private_keys)) != SILC_LIST_END) {
+      /* Parse the channel message payload. This also decrypts the payload */
+      payload = silc_channel_payload_parse(buffer, entry->cipher,
+                                          entry->hmac);
+      if (payload)
+       break;
+    }
+    if (entry == SILC_LIST_END)
+      goto out;
+  }
 
   message = silc_channel_get_data(payload, NULL);
 
@@ -285,7 +330,10 @@ void silc_client_receive_channel_key(SilcClient client,
    encrypted using that key. All clients on the channel must also know the
    key in order to decrypt the messages. However, it is possible to have
    several private keys per one channel. In this case only some of the
-   clients on the channel may now the one key and only some the other key.
+   clients on the channel may know the one key and only some the other key.
+
+   if `cipher' and/or `hmac' is NULL then default values will be used 
+   (aes-256-cbc for cipher and hmac-sha1-96 for hmac).
 
    The private key for channel is optional. If it is not set then the
    channel messages are encrypted using the channel key generated by the
@@ -309,9 +357,63 @@ int silc_client_add_channel_private_key(SilcClient client,
                                        SilcClientConnection conn,
                                        SilcChannelEntry channel,
                                        char *cipher,
+                                       char *hmac,
                                        unsigned char *key,
                                        unsigned int key_len)
 {
+  SilcChannelPrivateKey entry;
+  unsigned char hash[32];
+
+  if (!(channel->mode & SILC_CHANNEL_MODE_PRIVKEY))
+    return FALSE;
+
+  if (!cipher)
+    cipher = "aes-256-cbc";
+  if (!hmac)
+    hmac = "hmac-sha1-96";
+
+  if (!silc_cipher_is_supported(cipher))
+    return FALSE;
+
+  if (!silc_hmac_is_supported(hmac))
+    return FALSE;
+
+  /* Remove the current key, if it exists. */
+  if (channel->channel_key) {
+    silc_cipher_free(channel->channel_key);
+    memset(channel->key, 0, channel->key_len / 8);
+    silc_free(channel->key);
+    channel->channel_key = NULL;
+    channel->key = NULL;
+    channel->key_len = 0;
+  }
+  if (channel->hmac)
+    silc_hmac_free(channel->hmac);
+
+  if (!channel->private_keys)
+    channel->private_keys = silc_dlist_init();
+
+  /* Save the key */
+  entry = silc_calloc(1, sizeof(*entry));
+  entry->key = silc_calloc(key_len, sizeof(*entry->key));
+  memcpy(entry->key, key, key_len);
+  entry->key_len = key_len;
+
+  /* Allocate the cipher and set the key*/
+  silc_cipher_alloc(cipher, &entry->cipher);
+  silc_cipher_set_key(entry->cipher, key, key_len * 8);
+
+  /* Generate HMAC key from the channel key data and set it */
+  silc_hmac_alloc(hmac, NULL, &entry->hmac);
+  silc_hash_make(entry->hmac->hash, key, key_len, hash);
+  silc_hmac_set_key(entry->hmac, hash, silc_hash_len(entry->hmac->hash));
+  memset(hash, 0, sizeof(hash));
+
+  /* Add to the private keys list */
+  silc_dlist_add(channel->private_keys, entry);
+
+  if (!channel->curr_key)
+    channel->curr_key = entry;
 
   return TRUE;
 }
@@ -324,6 +426,25 @@ int silc_client_del_channel_private_keys(SilcClient client,
                                         SilcClientConnection conn,
                                         SilcChannelEntry channel)
 {
+  SilcChannelPrivateKey entry;
+
+  if (!channel->private_keys)
+    return FALSE;
+
+  silc_dlist_start(channel->private_keys);
+  while ((entry = silc_dlist_get(channel->private_keys)) != SILC_LIST_END) {
+    silc_dlist_del(channel->private_keys, entry);
+    memset(entry->key, 0, entry->key_len);
+    silc_free(entry->key);
+    silc_cipher_free(entry->cipher);
+    silc_hmac_free(entry->hmac);
+    silc_free(entry);
+  }
+
+  channel->curr_key = NULL;
+
+  silc_dlist_uninit(channel->private_keys);
+  channel->private_keys = NULL;
 
   return TRUE;
 }
@@ -339,8 +460,34 @@ int silc_client_del_channel_private_key(SilcClient client,
                                        SilcChannelEntry channel,
                                        SilcChannelPrivateKey key)
 {
+  SilcChannelPrivateKey entry;
+
+  if (!channel->private_keys)
+    return FALSE;
+
+  silc_dlist_start(channel->private_keys);
+  while ((entry = silc_dlist_get(channel->private_keys)) != SILC_LIST_END) {
+    if (entry == key) {
+      if (channel->curr_key == entry)
+       channel->curr_key = NULL;
+
+      silc_dlist_del(channel->private_keys, entry);
+      memset(entry->key, 0, entry->key_len);
+      silc_free(entry->key);
+      silc_cipher_free(entry->cipher);
+      silc_hmac_free(entry->hmac);
+      silc_free(entry);
+
+      if (silc_dlist_count(channel->private_keys) == 0) {
+       silc_dlist_uninit(channel->private_keys);
+       channel->private_keys = NULL;
+      }
+
+      return TRUE;
+    }
+  }
 
-  return TRUE;
+  return FALSE;
 }
 
 /* Returns array (pointers) of private keys associated to the `channel'.
@@ -355,8 +502,23 @@ silc_client_list_channel_private_keys(SilcClient client,
                                      SilcChannelEntry channel,
                                      unsigned int *key_count)
 {
+  SilcChannelPrivateKey *keys = NULL, entry;
+  unsigned int count = 0;
 
-  return NULL;
+  if (!channel->private_keys)
+    return NULL;
+
+  silc_dlist_start(channel->private_keys);
+  while ((entry = silc_dlist_get(channel->private_keys)) != SILC_LIST_END) {
+    keys = silc_realloc(keys, sizeof(*keys) * (count + 1));
+    keys[count] = entry;
+    count++;
+  }
+
+  if (key_count)
+    *key_count = count;
+
+  return keys;
 }
 
 /* Frees the SilcChannelPrivateKey array. */
@@ -364,5 +526,5 @@ silc_client_list_channel_private_keys(SilcClient client,
 void silc_client_free_channel_private_keys(SilcChannelPrivateKey *keys,
                                           unsigned int key_count)
 {
-
+  silc_free(keys);
 }
index 487b8b5b7e6ae94bb77a2f6a035e2b5dbcb675fb..c80b1c620f2cbb8ac6746aa11e5c468ed08d6bef 100644 (file)
@@ -178,6 +178,7 @@ SILC_TASK_CALLBACK(silc_client_process_key_agreement)
   proto_ctx->responder = TRUE;
   proto_ctx->context = context;
   proto_ctx->send_packet = silc_client_key_agreement_send_packet;
+  proto_ctx->verify = silc_client_protocol_ke_verify_key;
 
   /* Prepare the connection for key exchange protocol. We allocate the
      protocol but will not start it yet. The connector will be the
@@ -514,6 +515,7 @@ void silc_client_perform_key_agreement_fd(SilcClient client,
   proto_ctx->responder = FALSE;
   proto_ctx->context = ke;
   proto_ctx->send_packet = silc_client_key_agreement_send_packet;
+  proto_ctx->verify = silc_client_protocol_ke_verify_key;
 
   /* Perform key exchange protocol. */
   silc_protocol_alloc(SILC_PROTOCOL_CLIENT_KEY_EXCHANGE, 
index 4f9666172e753c96e1c0ef125cb358c29b467f4e..d6247a822ff1e170c3759ebfa06f4debb216a7f7 100644 (file)
@@ -741,13 +741,15 @@ 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;
   SilcChannelEntry channel;
-  unsigned int argc, mode, len;
+  SilcIDCacheEntry id_cache = NULL;
+  SilcChannelUser chu;
+  unsigned int argc, mode, len, list_count;
   char *topic, *tmp, *channel_name = NULL, *hmac;
-  SilcBuffer keyp;
+  SilcBuffer keyp, client_id_list, client_mode_list;
+  int i;
 
   SILC_LOG_DEBUG(("Start"));
 
@@ -760,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;
@@ -794,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);
@@ -812,7 +814,7 @@ SILC_CLIENT_CMD_REPLY_FUNC(join)
   silc_buffer_put(keyp, tmp, len);
 
   /* Get topic */
-  topic = silc_argument_get_arg_type(cmd->args, 9, NULL);
+  topic = silc_argument_get_arg_type(cmd->args, 10, NULL);
 
   /* Save received Channel ID. This actually creates the channel */
   channel = silc_client_new_channel_id(cmd->client, cmd->sock, channel_name, 
@@ -820,7 +822,7 @@ SILC_CLIENT_CMD_REPLY_FUNC(join)
   silc_id_payload_free(idp);
 
   /* Get hmac */
-  hmac = silc_argument_get_arg_type(cmd->args, 10, NULL);
+  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, 
@@ -832,20 +834,92 @@ SILC_CLIENT_CMD_REPLY_FUNC(join)
     }
   }
 
+  /* 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;
+      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);
-  silc_buffer_free(keyp);
-
-  if (topic)
-    client->ops->say(cmd->client, conn, 
-                    "Topic for %s: %s", channel_name, topic);
 
   /* Notify application */
-  COMMAND_REPLY((ARGS, channel_name, channel, mode, NULL, NULL, topic, hmac));
+  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);
@@ -1341,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);
index 7a2fc657070290709f7ef62d23f017830bdb9901..29752426126ab5a8f3ef65d5968108bda46b8e2f 100644 (file)
@@ -170,6 +170,167 @@ SilcClientEntry *silc_client_get_clients_local(SilcClient client,
   return clients;
 }
 
+typedef struct {
+  SilcClient client;
+  SilcClientConnection conn;
+  unsigned int list_count;
+  SilcBuffer client_id_list;
+  SilcGetClientCallback completion;
+  void *context;
+  int found;
+} *GetClientsByListInternal;
+
+SILC_CLIENT_CMD_FUNC(get_clients_list_callback)
+{
+  GetClientsByListInternal i = (GetClientsByListInternal)context;
+  SilcIDCacheEntry id_cache = NULL;
+  SilcBuffer client_id_list = i->client_id_list;
+  SilcClientEntry *clients = NULL;
+  unsigned int clients_count = 0;
+  int c;
+
+  for (c = 0; c < i->list_count; c++) {
+    unsigned short idp_len;
+    SilcClientID *client_id;
+
+    /* Get 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;
+
+    /* Get the client entry */
+    if (silc_idcache_find_by_id_one(i->conn->client_cache, (void *)client_id,
+                                   SILC_ID_CLIENT, &id_cache)) {
+      clients = silc_realloc(clients, sizeof(*clients) * 
+                            (clients_count + 1));
+      clients[clients_count] = (SilcClientEntry)id_cache->context;
+      clients_count++;
+      i->found = TRUE;
+    }
+
+    silc_free(client_id);
+    silc_buffer_pull(client_id_list, idp_len);
+  }
+
+  if (i->found) {
+    i->completion(i->client, i->conn, clients, clients_count, i->context);
+    silc_free(clients);
+  }
+}
+
+static void silc_client_get_clients_list_destructor(void *context)
+{
+  GetClientsByListInternal i = (GetClientsByListInternal)context;
+
+  if (i->found == FALSE)
+    i->completion(i->client, i->conn, NULL, 0, i->context);
+
+  if (i->client_id_list)
+    silc_buffer_free(i->client_id_list);
+  silc_free(i);
+}
+
+/* Gets client entries by the list of client ID's `client_id_list'. This
+   always resolves those client ID's it does not know yet from the server
+   so this function might take a while. The `client_id_list' is a list
+   of ID Payloads added one after other.  JOIN command reply and USERS
+   command reply for example returns this sort of list. The `completion'
+   will be called after the entries are available. */
+
+void silc_client_get_clients_by_list(SilcClient client,
+                                    SilcClientConnection conn,
+                                    unsigned int list_count,
+                                    SilcBuffer client_id_list,
+                                    SilcGetClientCallback completion,
+                                    void *context)
+{
+  SilcIDCacheEntry id_cache = NULL;
+  int i;
+  unsigned char **res_argv = NULL;
+  unsigned int *res_argv_lens = NULL, *res_argv_types = NULL, res_argc = 0;
+  GetClientsByListInternal in;
+
+  in = silc_calloc(1, sizeof(*in));
+  in->client = client;
+  in->conn = conn;
+  in->list_count = list_count;
+  in->client_id_list = silc_buffer_copy(client_id_list);
+  in->completion = completion;
+  in->context = context;
+
+  for (i = 0; i < list_count; i++) {
+    unsigned short idp_len;
+    SilcClientID *client_id;
+    SilcClientEntry entry;
+
+    /* Get 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;
+
+    /* Check if we have this client cached already. */
+    id_cache = NULL;
+    silc_idcache_find_by_id_one(conn->client_cache, (void *)client_id,
+                               SILC_ID_CLIENT, &id_cache);
+
+    /* If we don't have the entry or it has incomplete info, then resolve
+       it from the server. */
+    entry = id_cache ? (SilcClientEntry)id_cache->context : NULL;
+    if (!id_cache || !entry->nickname) {
+      /* No we don't have it, query it from the server. Assemble argument
+        table that will be sent fr the IDENTIFY command later. */
+      res_argv = silc_realloc(res_argv, sizeof(*res_argv) *
+                             (res_argc + 1));
+      res_argv_lens = silc_realloc(res_argv_lens, sizeof(*res_argv_lens) *
+                                  (res_argc + 1));
+      res_argv_types = silc_realloc(res_argv_types, sizeof(*res_argv_types) *
+                                   (res_argc + 1));
+      res_argv[res_argc] = client_id_list->data;
+      res_argv_lens[res_argc] = idp_len;
+      res_argv_types[res_argc] = res_argc + 3;
+      res_argc++;
+    }
+
+    silc_free(client_id);
+    silc_buffer_pull(client_id_list, idp_len);
+  }
+  silc_buffer_push(client_id_list, client_id_list->data - 
+                  client_id_list->head);
+
+  /* Query the client information from server if the list included clients
+     that we don't know about. */
+  if (res_argc) {
+    SilcBuffer res_cmd;
+
+    /* Send the IDENTIFY command to server */
+    res_cmd = silc_command_payload_encode(SILC_COMMAND_IDENTIFY,
+                                         res_argc, res_argv, res_argv_lens,
+                                         res_argv_types, ++conn->cmd_ident);
+    silc_client_packet_send(client, conn->sock, SILC_PACKET_COMMAND, 
+                           NULL, 0, NULL, NULL, res_cmd->data, res_cmd->len,
+                           TRUE);
+
+    silc_client_command_pending(conn, SILC_COMMAND_IDENTIFY, 
+                               conn->cmd_ident, 
+                               silc_client_get_clients_list_destructor,
+                               silc_client_command_get_clients_list_callback, 
+                               (void *)in);
+
+    silc_buffer_free(res_cmd);
+    silc_free(res_argv);
+    silc_free(res_argv_lens);
+    silc_free(res_argv_types);
+    return;
+  }
+
+  /* We have the clients in cache, get them and call the completion */
+  silc_client_command_get_clients_list_callback((void *)in);
+}
+
 /* The old style function to find client entry. This is used by the
    library internally. If `query' is TRUE then the client information is
    requested by the server. The pending command callback must be set
index f7d693370b0d4a8c87c02719c3d9045bfc65304b..513a52ef29090af8f9947e02410013bd4120dc3f 100644 (file)
@@ -49,10 +49,16 @@ typedef struct SilcChannelUserStruct {
   struct SilcChannelUserStruct *next;
 } *SilcChannelUser;
 
+/* Structure to hold one channel private key. */
+typedef struct {
+  SilcCipher cipher;                 /* The cipher and key */
+  SilcHmac hmac;                     /* The HMAC and hmac key */
+  unsigned char *key;                /* The key data */
+  unsigned int key_len;                      /* The key length */
+} *SilcChannelPrivateKey;
+
 /* Channel entry context. This is allocate for every channel client has
    joined to. This includes for example the channel specific keys */
-/* XXX channel_key is the server generated key. Later this context must 
-   include the channel private key. */
 typedef struct SilcChannelEntryStruct {
   char *channel_name;
   SilcChannelID *id;
@@ -63,11 +69,13 @@ typedef struct SilcChannelEntryStruct {
   SilcList clients;
 
   /* Channel keys */
-  SilcCipher channel_key;
-  unsigned char *key;
+  SilcCipher channel_key;                    /* The channel key */
+  unsigned char *key;                       /* Raw key data */
   unsigned int key_len;
-  unsigned char iv[SILC_CIPHER_MAX_IV_SIZE];
-  SilcHmac hmac;
+  unsigned char iv[SILC_CIPHER_MAX_IV_SIZE]; /* Current IV */
+  SilcHmac hmac;                            /* Current HMAC */
+  SilcDList private_keys;                   /* List of private keys or NULL */
+  SilcChannelPrivateKey curr_key;           /* Current private key */
 } *SilcChannelEntry;
 
 /* Prototypes (some functions are defined in the silcapi.h) */
index a408802684560e4685614280028bee2eafd8bbee..5040a3014da88186d47b24c2db588a95b49e0b7d 100644 (file)
@@ -55,12 +55,11 @@ void silc_client_protocol_ke_send_packet(SilcSKE ske,
 /* Callback that is called when we have received KE2 payload from
    responder. We try to verify the public key now. */
 
-static SilcSKEStatus 
-silc_client_protocol_ke_verify_key(SilcSKE ske,
-                                  unsigned char *pk_data,
-                                  unsigned int pk_len,
-                                  SilcSKEPKType pk_type,
-                                  void *context)
+SilcSKEStatus silc_client_protocol_ke_verify_key(SilcSKE ske,
+                                                unsigned char *pk_data,
+                                                unsigned int pk_len,
+                                                SilcSKEPKType pk_type,
+                                                void *context)
 {
   SilcProtocol protocol = (SilcProtocol)context;
   SilcClientKEInternalContext *ctx = 
@@ -313,8 +312,7 @@ SILC_TASK_CALLBACK(silc_client_protocol_key_exchange)
        /* Finish the protocol. This verifies the Key Exchange 2 payload
           sent by responder. */
        status = silc_ske_initiator_finish(ctx->ske, ctx->packet->buffer,
-                                          silc_client_protocol_ke_verify_key,
-                                          context, NULL, NULL);
+                                          ctx->verify, context, NULL, NULL);
       }
 
       if (status != SILC_SKE_STATUS_OK) {
index 92c195abb6615e6dcf91cd0d790db0ed871d75d8..4fa009fbf4f5490372e5d50bdedb2200308a9d57 100644 (file)
@@ -41,6 +41,7 @@ typedef struct {
   SilcPacketContext *packet;
 
   SilcSKESendPacketCb send_packet;  /* SKE's packet sending callback */
+  SilcSKEVerifyCb verify;          /* SKE's key verify callback */
   SilcSKE ske;                     /* The SKE object */
   SilcSKEKeyMaterial *keymat;      /* The negotiated key material */
   void *context;                   /* Internal context */
@@ -78,6 +79,11 @@ void silc_client_protocol_ke_send_packet(SilcSKE ske,
                                         SilcBuffer packet,
                                         SilcPacketType type,
                                         void *context);
+SilcSKEStatus silc_client_protocol_ke_verify_key(SilcSKE ske,
+                                                unsigned char *pk_data,
+                                                unsigned int pk_len,
+                                                SilcSKEPKType pk_type,
+                                                void *context);
 void silc_client_protocol_ke_set_keys(SilcSKE ske,
                                      SilcSocketConnection sock,
                                      SilcSKEKeyMaterial *keymat,
index 67372a55c53f699b3e219352993030e89050304d..bdef1c76076a424672fccc8b27b6a84597b4a730 100644 (file)
@@ -35,6 +35,8 @@
    of how to use the SILC Client Library.
 */
 
+/* General definitions */
+
 /* Key agreement callback that is called after the key agreement protocol
    has been performed. This is called also if error occured during the
    key agreement protocol. The `key' is the allocated key material and
@@ -48,6 +50,19 @@ typedef void (*SilcKeyAgreementCallback)(SilcClient client,
                                         SilcSKEKeyMaterial *key,
                                         void *context);
 
+/* Structure to hold the list of private message keys. The array of this
+   structure is returned by the silc_client_list_private_message_keys
+   function. */
+typedef struct {
+  SilcClientEntry client_entry;       /* The remote client entry */
+  char *cipher;                              /* The cipher name */
+  unsigned char *key;                /* The original key, If the appliation
+                                        provided it. This is NULL if the
+                                        library generated the key or if
+                                        the SKE key material was used. */
+  unsigned int key_len;                      /* The key length */
+} *SilcPrivateMessageKeys;
+
 /******************************************************************************
 
                            SILC Client Operations
@@ -263,10 +278,17 @@ void silc_client_close_connection(SilcClient client,
    encrypted with the next receiver's key and the rest of the packet is
    encrypted with the channel specific key. Padding and HMAC is computed
    with the next receiver's key. The `data' is the channel message. If
-   the `force_send' is TRUE then the packet is sent immediately. */
+   the `force_send' is TRUE then the packet is sent immediately. 
+
+   If `key' is provided then that private key is used to encrypt the
+   channel message.  If it is not provided, private keys has not been
+   set at all, the normal channel key is used automatically.  If private
+   keys are set then the first key (the key that was added first as
+   private key) is used. */
 void silc_client_send_channel_message(SilcClient client, 
                                      SilcClientConnection conn,
                                      SilcChannelEntry channel,
+                                     SilcChannelPrivateKey key,
                                      unsigned char *data, 
                                      unsigned int data_len, 
                                      int force_send);
@@ -321,6 +343,19 @@ SilcClientEntry *silc_client_get_clients_local(SilcClient client,
                                               char *server,
                                               unsigned int *clients_count);
 
+/* Gets client entries by the list of client ID's `client_id_list'. This
+   always resolves those client ID's it does not know yet from the server
+   so this function might take a while. The `client_id_list' is a list
+   of ID Payloads added one after other.  JOIN command reply and USERS
+   command reply for example returns this sort of list. The `completion'
+   will be called after the entries are available. */
+void silc_client_get_clients_by_list(SilcClient client,
+                                    SilcClientConnection conn,
+                                    unsigned int list_count,
+                                    SilcBuffer client_id_list,
+                                    SilcGetClientCallback completion,
+                                    void *context);
+
 /* Find entry for client by the client's ID. Returns the entry or NULL
    if the entry was not found. */
 SilcClientEntry silc_client_get_client_by_id(SilcClient client,
@@ -447,19 +482,6 @@ int silc_client_del_private_message_key(SilcClient client,
                                        SilcClientConnection conn,
                                        SilcClientEntry client_entry);
 
-/* Structure to hold the list of private message keys. The array of this
-   structure is returned by the silc_client_list_private_message_keys
-   function. */
-typedef struct {
-  SilcClientEntry client_entry;       /* The remote client entry */
-  char *cipher;                              /* The cipher name */
-  unsigned char *key;                /* The original key, If the appliation
-                                        provided it. This is NULL if the
-                                        library generated the key or if
-                                        the SKE key material was used. */
-  unsigned int key_len;                      /* The key length */
-} *SilcPrivateMessageKeys;
-
 /* Returns array of set private message keys associated to the connection
    `conn'. Returns allocated SilcPrivateMessageKeys array and the array
    count to the `key_count' argument. The array must be freed by the caller
@@ -478,7 +500,8 @@ void silc_client_free_private_message_keys(SilcPrivateMessageKeys keys,
                                           unsigned int key_count);
 
 
-/* Channel private key management (client_channel.c) */
+/* Channel private key management (client_channel.c, 
+   SilcChannelPrivateKey is defined in idlist.h) */
 
 /* Adds private key for channel. This may be set only if the channel's mode
    mask includes the SILC_CHANNEL_MODE_PRIVKEY. This returns FALSE if the
@@ -486,7 +509,7 @@ void silc_client_free_private_message_keys(SilcPrivateMessageKeys keys,
    encrypted using that key. All clients on the channel must also know the
    key in order to decrypt the messages. However, it is possible to have
    several private keys per one channel. In this case only some of the
-   clients on the channel may now the one key and only some the other key.
+   clients on the channel may know the one key and only some the other key.
 
    The private key for channel is optional. If it is not set then the
    channel messages are encrypted using the channel key generated by the
@@ -509,6 +532,7 @@ int silc_client_add_channel_private_key(SilcClient client,
                                        SilcClientConnection conn,
                                        SilcChannelEntry channel,
                                        char *cipher,
+                                       char *hmac,
                                        unsigned char *key,
                                        unsigned int key_len);
 
@@ -519,13 +543,6 @@ int silc_client_del_channel_private_keys(SilcClient client,
                                         SilcClientConnection conn,
                                         SilcChannelEntry channel);
 
-/* Structure to hold one channel private key. */
-typedef struct {
-  char *cipher;                              /* The cipher name */
-  unsigned char *key;                /* The key */
-  unsigned int key_len;                      /* The key length */
-} *SilcChannelPrivateKey;
-
 /* Removes and frees private key `key' from the channel `channel'. The `key'
    is retrieved by calling the function silc_client_list_channel_private_keys.
    The key is not used after this. If the key was last private key then the