updates.
authorPekka Riikonen <priikone@silcnet.org>
Wed, 11 Jul 2001 22:24:29 +0000 (22:24 +0000)
committerPekka Riikonen <priikone@silcnet.org>
Wed, 11 Jul 2001 22:24:29 +0000 (22:24 +0000)
22 files changed:
CHANGES
TODO
apps/irssi/docs/help/in/getkey.in
apps/irssi/src/silc/core/client_ops.c
apps/irssi/src/silc/core/silc-core.c
apps/irssi/src/silc/core/silc-servers.c
apps/silc/Makefile.am
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/packet_send.h
doc/draft-riikonen-silc-commands-01.nroff
lib/silcclient/client.c
lib/silcclient/client.h
lib/silcclient/command.c
lib/silcclient/command.h
lib/silcclient/command_reply.c
lib/silcclient/idlist.c
lib/silcclient/idlist.h
lib/silcclient/silcapi.h

diff --git a/CHANGES b/CHANGES
index f170b1d9ee6932d8ff350d78f7d289deff96304d..fe7056cda64839569db99f879ca91e43101c3a30 100644 (file)
--- a/CHANGES
+++ b/CHANGES
@@ -1,3 +1,44 @@
+Wed Jul 11 18:31:57 EEST 2001  Pekka Riikonen <priikone@silcnet.org>
+
+       * Added SilcClientParams structure to the lib/silcclient/silcapi.h
+         which is given as argument to the silc_client_alloc now.
+         It can be used to configure the client and set various parameters
+         that affect the function of the client.
+
+       * The USERS command in server did not check whether the channel
+         is private or secret.  Affected file silcd/command.c.
+
+       * Added new argument to the USERS command in protocol specification.
+         The USERS command now can take the channel name as argument
+         as well.  Added support for this in client and server and
+         updated the protocol specs.
+
+       * Completed the GETKEY command in client. It can be now used
+         to fetch also servers public key not only some clients. 
+         Affected files lib/silcclient/command[_reply].c.
+
+       * Added silc_client_get_server to return server entry by the
+         server name.  Affected files lib/silcclient/silcapi.h and
+         idlist.c.
+
+       * Redefined the IDENTIFY command in protocol specification to be
+         more generic.  It now can be used to query information about
+         any entity in the SILC Network, including clients, servers and
+         channels.  The query may be based either the entity's name
+         or the ID.  Added support for this in both client and server.
+
+         Affected files silcd/command.c and lib/silcclient/command.c
+         and command_reply.c.
+
+       * Optimized the WHOIS and WHOWAS commands in the server. Removed
+         the _from_client and _from_server functions.  Affected file
+         silcd/command.c.
+
+       * Added silc_client_get_channel_by_id_resolve to the file
+         lib/silcclient/silcapi.h to resolve channel information by
+         its ID.  Added also silc_client_get_channel_by_id that
+         does not resolve it from the server.
+
 Tue Jul 10 18:05:38 EEST 2001  Pekka Riikonen <priikone@silcnet.org>
 
        * Added SilcServerEntry context into the client library
diff --git a/TODO b/TODO
index b6c1447d4f20b558a408eecb4ade3fc92af00137..4f205059f5cc2365d2854cc5ebe62039f43d717e 100644 (file)
--- a/TODO
+++ b/TODO
@@ -22,10 +22,8 @@ TODO/bugs in Irssi SILC client
 TODO/bugs In SILC Client Library
 ================================
 
- o All protocol execution timeouts are hard coded. They should be 
-   configurable and the Irssi SILC client should be able to set them
-   with for example /set key_exchange_timeout etc.  The silc_client_alloc
-   should take a Params structure or something as argument.
+ o The public key authentication is missing for example in OPER and SILCOPER
+   commands.  See the XXX's in the lib/silcclient/command.c.
 
  o The client library must manage somehow when receiving client that has
    same nickname, same server, same username but different Client ID than
index d2c5ee0a114134f185c4f4365edb56da52d611ff..38b63b49912e06e44149a67ca90bb961e6a4c7c9 100644 (file)
@@ -1,10 +1,9 @@
 
 @SYNTAX:getkey@
 
-This command is used to fetch remote client's public key.
-The public key is fetched from the server the client is
-connected to.  This way the public key might have been
-verified already.  However, you will be prompted to verify
+This command is used to fetch remote client's or server's public
+key.  When fetching client's public key it is fetched from the
+server the client is connected to.  This way the public key might
+have been verified already.  However, you will be prompted to verify
 the fetched public key.  The public key is saved into your
 local key directory (~/.silc/clientkeys/).
-
index b873e2cb9ed15ba194820b057a7866d042be3c02..e41cd55a2fa93af1283f2643bab26db791831d59 100644 (file)
@@ -676,10 +676,12 @@ silc_command_reply(SilcClient client, SilcClientConnection conn,
       
       pk = silc_pkcs_public_key_encode(public_key, &pk_len);
       
-      if (id_type == SILC_ID_CLIENT)
-       silc_verify_public_key_internal(client, conn, SILC_SOCKET_TYPE_CLIENT,
-                                       pk, pk_len, SILC_SKE_PK_TYPE_SILC,
-                                       NULL, NULL);
+      silc_verify_public_key_internal(client, conn, 
+                                     (id_type == SILC_ID_CLIENT ?
+                                      SILC_SOCKET_TYPE_CLIENT :
+                                      SILC_SOCKET_TYPE_SERVER),
+                                     pk, pk_len, SILC_SKE_PK_TYPE_SILC,
+                                     NULL, NULL);
       silc_free(pk);
     }
     break;
index d29ad5ee82a1031e4856773910cb55670ae11748..da42b3b3135b3b59d9407716ca42e02bf1e43901 100644 (file)
@@ -271,7 +271,7 @@ void silc_core_init_finish(void)
   silc_init_userinfo();
 
   /* Allocate SILC client */
-  silc_client = silc_client_alloc(&ops, NULL, silc_version_string);
+  silc_client = silc_client_alloc(&ops, NULL, NULL, silc_version_string);
 
   /* Load local config file */
   silc_config = silc_client_config_alloc(SILC_CLIENT_HOME_CONFIG_FILE);
index 566b269a5c353dda7b44ae3ea660065774155839..574979c76658cf8065ac39ae42d1567e134e1baf 100644 (file)
@@ -253,7 +253,7 @@ char *silc_server_get_channels(SILC_SERVER_REC *server)
 /* SYNTAX: BAN <channel> [+|-[<nickname>[@<server>[!<username>[@hostname>]]]]] */
 /* SYNTAX: CMODE <channel> +|-<modes> [{ <arguments>}] */
 /* SYNTAX: CUMODE <channel> +|-<modes> <nickname>[@<server>] [-pubkey|<passwd>] */
-/* SYNTAX: GETKEY <nickname> */
+/* SYNTAX: GETKEY <nickname or server name> */
 /* SYNTAX: INVITE <channel> [<nickname>[@server>] */
 /* SYNTAX: INVITE <channel> [+|-[<nickname>[@<server>[!<username>[@hostname>]]]]] */
 /* SYNTAX: KEY MSG <nickname> set|unset|list|agreement|negotiate [<arguments>] */
index e216b583fc03d40675b54d916fe673de53084537..ced6a4b1bc62c90b35ab99450b0f3c06db7b5fbf 100644 (file)
 
 AUTOMAKE_OPTIONS = 1.0 no-dependencies foreign
 
-bin_PROGRAMS = silc
-
-silc_SOURCES = \
-       silc.c \
-       clientconfig.c \
-       clientutil.c \
-       local_command.c \
-       screen.c \
-       client_ops.c
-
-silc_DEPENDENCIES = ../lib/libsilcclient.a ../lib/libsilc.a
+#bin_PROGRAMS = silc
+#silc_SOURCES = \
+#      silc.c \
+#      clientconfig.c \
+#      clientutil.c \
+#      local_command.c \
+#      screen.c \
+#      client_ops.c
+#silc_DEPENDENCIES = ../lib/libsilcclient.a ../lib/libsilc.a
 
 LIBS = $(SILC_COMMON_LIBS)
 LDADD = -L. -L.. -L../lib -lsilcclient
index 52a4a78f585434c6a7a4873a77a886941abc5bff..bdb236d3cbeb25a69869b00843f4ee407e7a0272 100644 (file)
@@ -422,7 +422,10 @@ silc_server_command_pending_error_check(SilcServerCommandContext cmd,
     return FALSE;
 
   SILC_GET16_MSB(status, silc_argument_get_arg_type(cmdr->args, 1, NULL));
-  if (status != SILC_STATUS_OK) {
+  if (status != SILC_STATUS_OK &&
+      status != SILC_STATUS_LIST_START &&
+      status != SILC_STATUS_LIST_ITEM &&
+      status != SILC_STATUS_LIST_END) {
     /* Send the error message */
     silc_server_command_send_status_reply(cmd, command, status);
     return TRUE;
@@ -503,8 +506,6 @@ silc_server_command_whois_parse(SilcServerCommandContext cmd,
        }
       }
     }
-
-    /* Command includes ID, use that */
   }
 
   /* Get the max count of reply messages allowed */
@@ -703,7 +704,7 @@ silc_server_command_whois_send_reply(SilcServerCommandContext cmd,
 }
 
 static int
-silc_server_command_whois_from_client(SilcServerCommandContext cmd)
+silc_server_command_whois_process(SilcServerCommandContext cmd)
 {
   SilcServer server = cmd->server;
   char *nick = NULL, *server_name = NULL;
@@ -712,12 +713,14 @@ silc_server_command_whois_from_client(SilcServerCommandContext cmd)
   SilcClientID **client_id = NULL;
   uint32 client_id_count = 0, clients_count = 0;
   int i, ret = 0;
+  bool check_global = FALSE;
 
   /* Protocol dictates that we must always send the received WHOIS request
      to our router if we are normal server, so let's do it now unless we
      are standalone. We will not send any replies to the client until we
      have received reply from the router. */
-  if (server->server_type == SILC_SERVER && !cmd->pending && 
+  if (cmd->sock->type == SILC_SOCKET_TYPE_CLIENT &&
+      server->server_type == SILC_SERVER && !cmd->pending && 
       !server->standalone) {
     SilcBuffer tmpbuf;
     uint16 old_ident;
@@ -750,110 +753,10 @@ silc_server_command_whois_from_client(SilcServerCommandContext cmd)
   /* We are ready to process the command request. Let's search for the
      requested client and send reply to the requesting client. */
 
-  /* Parse the whois request */
-  if (!silc_server_command_whois_parse(cmd, &client_id, &client_id_count, 
-                                      &nick, &server_name, &count,
-                                      SILC_COMMAND_WHOIS))
-    return 0;
-
-  /* Get all clients matching that ID or nickname from local list */
-  if (client_id_count) {
-    /* Check all Client ID's received in the command packet */
-    for (i = 0; i < client_id_count; i++) {
-      entry = silc_idlist_find_client_by_id(server->local_list, 
-                                           client_id[i], NULL);
-      if (entry) {
-       clients = silc_realloc(clients, sizeof(*clients) * 
-                              (clients_count + 1));
-       clients[clients_count++] = entry;
-      }
-    }
-  } else {
-    if (!silc_idlist_get_clients_by_hash(server->local_list, 
-                                        nick, server->md5hash,
-                                        &clients, &clients_count))
-      silc_idlist_get_clients_by_nickname(server->local_list, 
-                                         nick, server_name,
-                                         &clients, &clients_count);
-  }
-  
-  /* Check global list as well */
-  if (client_id_count) {
-    /* Check all Client ID's received in the command packet */
-    for (i = 0; i < client_id_count; i++) {
-      entry = silc_idlist_find_client_by_id(server->global_list, 
-                                           client_id[i], NULL);
-      if (entry) {
-       clients = silc_realloc(clients, sizeof(*clients) * 
-                              (clients_count + 1));
-       clients[clients_count++] = entry;
-      }
-    }
-  } else {
-    if (!silc_idlist_get_clients_by_hash(server->global_list, 
-                                        nick, server->md5hash,
-                                        &clients, &clients_count))
-      silc_idlist_get_clients_by_nickname(server->global_list, 
-                                         nick, server_name,
-                                         &clients, &clients_count);
-  }
-  
-  if (!clients) {
-    /* Such client(s) really does not exist in the SILC network. */
-    if (!client_id_count) {
-      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[0], 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;
-  }
-
-  /* Router always finds the client entry if it exists in the SILC network.
-     However, it might be incomplete entry and does not include all the
-     mandatory fields that WHOIS command reply requires. Check for these and
-     make query from the server who owns the client if some fields are 
-     missing. */
-  if (!silc_server_command_whois_check(cmd, clients, clients_count)) {
-    ret = -1;
-    goto out;
-  }
-
-  /* Send the command reply to the client */
-  silc_server_command_whois_send_reply(cmd, clients, clients_count,
-                                      count);
-
- out:
-  if (client_id_count) {
-    for (i = 0; i < client_id_count; i++)
-      silc_free(client_id[i]);
-    silc_free(client_id);
-  }
-  if (clients)
-    silc_free(clients);
-  if (nick)
-    silc_free(nick);
-  if (server_name)
-    silc_free(server_name);
-
-  return ret;
-}
-
-static int
-silc_server_command_whois_from_server(SilcServerCommandContext cmd)
-{
-  SilcServer server = cmd->server;
-  char *nick = NULL, *server_name = NULL;
-  int count = 0;
-  SilcClientEntry *clients = NULL, entry;
-  SilcClientID **client_id = NULL;
-  uint32 client_id_count = 0, clients_count = 0;
-  int i, ret = 0;
+  if (cmd->sock->type == SILC_SOCKET_TYPE_CLIENT)
+    check_global = TRUE;
+  else if (server->server_type == SILC_ROUTER)
+    check_global = TRUE;
 
   /* Parse the whois request */
   if (!silc_server_command_whois_parse(cmd, &client_id, &client_id_count, 
@@ -861,14 +764,15 @@ silc_server_command_whois_from_server(SilcServerCommandContext cmd)
                                       SILC_COMMAND_WHOIS))
     return 0;
 
-  /* Process the command request. Let's search for the requested client and
-     send reply to the requesting server. */
-
+  /* Get all clients matching that ID or nickname from local list */
   if (client_id_count) {
     /* Check all Client ID's received in the command packet */
     for (i = 0; i < client_id_count; i++) {
       entry = silc_idlist_find_client_by_id(server->local_list, 
                                            client_id[i], NULL);
+      if (!entry && check_global)
+       entry = silc_idlist_find_client_by_id(server->global_list, 
+                                             client_id[i], NULL);
       if (entry) {
        clients = silc_realloc(clients, sizeof(*clients) * 
                               (clients_count + 1));
@@ -882,22 +786,7 @@ silc_server_command_whois_from_server(SilcServerCommandContext cmd)
       silc_idlist_get_clients_by_nickname(server->local_list, 
                                          nick, server_name,
                                          &clients, &clients_count);
-  }
-  
-  /* If we are router we will check our global list as well. */
-  if (server->server_type == SILC_ROUTER) {
-    if (client_id_count) {
-      /* Check all Client ID's received in the command packet */
-      for (i = 0; i < client_id_count; i++) {
-       entry = silc_idlist_find_client_by_id(server->global_list, 
-                                             client_id[i], NULL);
-       if (entry) {
-         clients = silc_realloc(clients, sizeof(*clients) * 
-                                (clients_count + 1));
-         clients[clients_count++] = entry;
-       }
-      }
-    } else {
+    if (check_global) {
       if (!silc_idlist_get_clients_by_hash(server->global_list, 
                                           nick, server->md5hash,
                                           &clients, &clients_count))
@@ -906,9 +795,9 @@ silc_server_command_whois_from_server(SilcServerCommandContext cmd)
                                            &clients, &clients_count);
     }
   }
-
+  
   if (!clients) {
-    /* Such a client really does not exist in the SILC network. */
+    /* Such client(s) really does not exist in the SILC network. */
     if (!client_id_count) {
       silc_server_command_send_status_data(cmd, SILC_COMMAND_WHOIS,
                                           SILC_STATUS_ERR_NO_SUCH_NICK,
@@ -933,7 +822,7 @@ silc_server_command_whois_from_server(SilcServerCommandContext cmd)
     goto out;
   }
 
-  /* Send the command reply to the client */
+  /* Send the command reply */
   silc_server_command_whois_send_reply(cmd, clients, clients_count,
                                       count);
 
@@ -963,11 +852,7 @@ SILC_SERVER_CMD_FUNC(whois)
 
   SILC_SERVER_COMMAND_CHECK(SILC_COMMAND_WHOIS, cmd, 1, 3328);
 
-  if (cmd->sock->type == SILC_SOCKET_TYPE_CLIENT)
-    ret = silc_server_command_whois_from_client(cmd);
-  else if ((cmd->sock->type == SILC_SOCKET_TYPE_SERVER) ||
-          (cmd->sock->type == SILC_SOCKET_TYPE_ROUTER))
-    ret = silc_server_command_whois_from_server(cmd);
+  ret = silc_server_command_whois_process(cmd);
 
   if (!ret)
     silc_server_command_free(cmd);
@@ -1165,7 +1050,7 @@ silc_server_command_whowas_send_reply(SilcServerCommandContext cmd,
 }
 
 static int
-silc_server_command_whowas_from_client(SilcServerCommandContext cmd)
+silc_server_command_whowas_process(SilcServerCommandContext cmd)
 {
   SilcServer server = cmd->server;
   char *nick = NULL, *server_name = NULL;
@@ -1173,12 +1058,14 @@ silc_server_command_whowas_from_client(SilcServerCommandContext cmd)
   SilcClientEntry *clients = NULL;
   uint32 clients_count = 0;
   int ret = 0;
+  bool check_global = FALSE;
 
   /* Protocol dictates that we must always send the received WHOWAS request
      to our router if we are normal server, so let's do it now unless we
      are standalone. We will not send any replies to the client until we
      have received reply from the router. */
-  if (server->server_type == SILC_SERVER && !cmd->pending && 
+  if (cmd->sock->type == SILC_SOCKET_TYPE_CLIENT &&
+      server->server_type == SILC_SERVER && !cmd->pending && 
       !server->standalone) {
     SilcBuffer tmpbuf;
     uint16 old_ident;
@@ -1211,6 +1098,11 @@ silc_server_command_whowas_from_client(SilcServerCommandContext cmd)
   /* We are ready to process the command request. Let's search for the
      requested client and send reply to the requesting client. */
 
+  if (cmd->sock->type == SILC_SOCKET_TYPE_CLIENT)
+    check_global = TRUE;
+  else if (server->server_type == SILC_ROUTER)
+    check_global = TRUE;
+
   /* Parse the whowas request */
   if (!silc_server_command_whowas_parse(cmd, &nick, &server_name, &count))
     return 0;
@@ -1224,66 +1116,7 @@ silc_server_command_whowas_from_client(SilcServerCommandContext cmd)
                                    &clients, &clients_count);
   
   /* Check global list as well */
-  if (!silc_idlist_get_clients_by_nickname(server->global_list, 
-                                          nick, server_name,
-                                          &clients, &clients_count))
-    silc_idlist_get_clients_by_hash(server->global_list, 
-                                   nick, server->md5hash,
-                                   &clients, &clients_count);
-  
-  if (!clients) {
-    /* Such a client really does not exist in the SILC network. */
-    silc_server_command_send_status_data(cmd, SILC_COMMAND_WHOWAS,
-                                        SILC_STATUS_ERR_NO_SUCH_NICK,
-                                        3, nick, strlen(nick));
-    goto out;
-  }
-
-  if (!silc_server_command_whowas_check(cmd, clients, clients_count)) {
-    ret = -1;
-    goto out;
-  }
-
-  /* Send the command reply to the client */
-  silc_server_command_whowas_send_reply(cmd, clients, clients_count);
-
- out:
-  if (clients)
-    silc_free(clients);
-  if (nick)
-    silc_free(nick);
-  if (server_name)
-    silc_free(server_name);
-
-  return ret;
-}
-
-static int
-silc_server_command_whowas_from_server(SilcServerCommandContext cmd)
-{
-  SilcServer server = cmd->server;
-  char *nick = NULL, *server_name = NULL;
-  int count = 0;
-  SilcClientEntry *clients = NULL;
-  uint32 clients_count = 0;
-  int ret = 0;
-
-  /* Parse the whowas request */
-  if (!silc_server_command_whowas_parse(cmd, &nick, &server_name, &count))
-    return 0;
-
-  /* Process the command request. Let's search for the requested client and
-     send reply to the requesting server. */
-
-  if (!silc_idlist_get_clients_by_nickname(server->local_list, 
-                                          nick, server_name,
-                                          &clients, &clients_count))
-    silc_idlist_get_clients_by_hash(server->local_list, 
-                                   nick, server->md5hash,
-                                   &clients, &clients_count);
-  
-  /* If we are router we will check our global list as well. */
-  if (server->server_type == SILC_ROUTER) {
+  if (check_global) {
     if (!silc_idlist_get_clients_by_nickname(server->global_list, 
                                             nick, server_name,
                                             &clients, &clients_count))
@@ -1291,7 +1124,7 @@ silc_server_command_whowas_from_server(SilcServerCommandContext cmd)
                                      nick, server->md5hash,
                                      &clients, &clients_count);
   }
-
+  
   if (!clients) {
     /* Such a client really does not exist in the SILC network. */
     silc_server_command_send_status_data(cmd, SILC_COMMAND_WHOWAS,
@@ -1300,6 +1133,12 @@ silc_server_command_whowas_from_server(SilcServerCommandContext cmd)
     goto out;
   }
 
+  if (cmd->sock->type == SILC_SOCKET_TYPE_CLIENT &&
+      !silc_server_command_whowas_check(cmd, clients, clients_count)) {
+    ret = -1;
+    goto out;
+  }
+
   /* Send the command reply to the client */
   silc_server_command_whowas_send_reply(cmd, clients, clients_count);
 
@@ -1323,11 +1162,7 @@ SILC_SERVER_CMD_FUNC(whowas)
 
   SILC_SERVER_COMMAND_CHECK(SILC_COMMAND_WHOWAS, cmd, 1, 2);
 
-  if (cmd->sock->type == SILC_SOCKET_TYPE_CLIENT)
-    ret = silc_server_command_whowas_from_client(cmd);
-  else if ((cmd->sock->type == SILC_SOCKET_TYPE_SERVER) ||
-          (cmd->sock->type == SILC_SOCKET_TYPE_ROUTER))
-    ret = silc_server_command_whowas_from_server(cmd);
+  ret = silc_server_command_whowas_process(cmd);
 
   if (!ret)
     silc_server_command_free(cmd);
@@ -1339,14 +1174,238 @@ SILC_SERVER_CMD_FUNC(whowas)
 
 ******************************************************************************/
 
-/* Checks that all mandatory fields are present. If not then send WHOIS 
-   request to the server who owns the client. We use WHOIS because we want
-   to get as much information as possible at once. */
+static bool
+silc_server_command_identify_parse(SilcServerCommandContext cmd,
+                                  SilcClientEntry **clients,
+                                  uint32 *clients_count,
+                                  SilcServerEntry **servers,
+                                  uint32 *servers_count,
+                                  SilcChannelEntry **channels,
+                                  uint32 *channels_count,
+                                  uint32 *count)
+{
+  SilcServer server = cmd->server;
+  unsigned char *tmp;
+  uint32 len;
+  uint32 argc = silc_argument_get_arg_num(cmd->args);
+  SilcIDPayload idp;
+  bool check_global = FALSE;
+  void *entry;
+  int i;
+  bool error = FALSE;
+
+  if (cmd->sock->type == SILC_SOCKET_TYPE_CLIENT)
+    check_global = TRUE;
+  else if (server->server_type == SILC_ROUTER)
+    check_global = TRUE;
+
+  /* If ID Payload is in the command it must be used instead of names */
+  tmp = silc_argument_get_arg_type(cmd->args, 5, &len);
+  if (!tmp) {
+    /* No ID, get the names. */
+
+    /* Try to get nickname@server. */
+    tmp = silc_argument_get_arg_type(cmd->args, 1, NULL);
+    if (tmp) {
+      char *nick = NULL;
+      char *nick_server = NULL;
+
+      if (strchr(tmp, '@')) {
+       len = strcspn(tmp, "@");
+       nick = silc_calloc(len + 1, sizeof(char));
+       memcpy(nick, tmp, len);
+       nick_server = silc_calloc(strlen(tmp) - len, sizeof(char));
+       memcpy(nick_server, tmp + len + 1, strlen(tmp) - len - 1);
+      } else {
+       nick = strdup(tmp);
+      }
+
+      if (!silc_idlist_get_clients_by_hash(server->local_list, 
+                                          nick, server->md5hash,
+                                          clients, clients_count))
+       silc_idlist_get_clients_by_nickname(server->local_list, 
+                                           nick, nick_server,
+                                           clients, clients_count);
+      if (check_global) {
+       if (!silc_idlist_get_clients_by_hash(server->global_list, 
+                                            nick, server->md5hash,
+                                            clients, clients_count))
+         silc_idlist_get_clients_by_nickname(server->global_list, 
+                                             nick, nick_server,
+                                             clients, clients_count);
+      }
+
+      silc_free(nick);
+      silc_free(nick_server);
+
+      if (!(*clients)) {
+       silc_server_command_send_status_data(cmd, SILC_COMMAND_IDENTIFY,
+                                            SILC_STATUS_ERR_NO_SUCH_NICK,
+                                            3, tmp, strlen(tmp));
+       return FALSE;
+      }
+    }
+
+    /* Try to get server name */
+    tmp = silc_argument_get_arg_type(cmd->args, 2, NULL);
+    if (tmp) {
+      entry = silc_idlist_find_server_by_name(server->local_list,
+                                             tmp, NULL);
+      if (!entry && check_global)
+       entry = silc_idlist_find_server_by_name(server->local_list,
+                                               tmp, NULL);
+      if (entry) {
+       *servers = silc_realloc(*servers, sizeof(**servers) * 
+                               (*servers_count + 1));
+       (*servers)[(*servers_count)++] = entry;
+      }
+
+      if (!(*servers)) {
+       silc_server_command_send_status_data(cmd, SILC_COMMAND_IDENTIFY,
+                                            SILC_STATUS_ERR_NO_SUCH_SERVER,
+                                            3, tmp, strlen(tmp));
+       return FALSE;
+      }
+    }
+
+    /* Try to get channel name */
+    tmp = silc_argument_get_arg_type(cmd->args, 3, NULL);
+    if (tmp) {
+      entry = silc_idlist_find_channel_by_name(server->local_list,
+                                              tmp, NULL);
+      if (!entry && check_global)
+       entry = silc_idlist_find_channel_by_name(server->local_list,
+                                                tmp, NULL);
+      if (entry) {
+       *channels = silc_realloc(*channels, sizeof(**channels) * 
+                                (*channels_count + 1));
+       (*channels)[(*channels_count)++] = entry;
+      }
+
+      if (!(*channels)) {
+       silc_server_command_send_status_data(cmd, SILC_COMMAND_IDENTIFY,
+                                            SILC_STATUS_ERR_NO_SUCH_CHANNEL,
+                                            3, tmp, strlen(tmp));
+       return FALSE;
+      }
+    }
+
+    if (!(*clients) && !(*servers) && !(*channels)) {
+      silc_server_command_send_status_reply(cmd, SILC_COMMAND_IDENTIFY,
+                                           SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
+      return FALSE;
+    }
+  } else {
+    /* Command includes ID, we must use that.  Also check whether the command
+       has more than one ID set - take them all. */
+
+    /* Take all ID's from the command packet */
+    for (i = 0; i < argc; i++) {
+      void *id;
+
+      tmp = silc_argument_get_arg_type(cmd->args, i + 5, &len);
+      if (!tmp)
+       continue;
+      
+      idp = silc_id_payload_parse_data(tmp, len);
+      if (!idp) {
+       silc_free(*clients);
+       silc_free(*servers);
+       silc_free(*channels);
+       silc_server_command_send_status_reply(cmd, SILC_COMMAND_IDENTIFY,
+                                     SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
+       return FALSE;
+      }
+
+      id = silc_id_payload_get_id(idp);
+      
+      switch (silc_id_payload_get_type(idp)) {
+       
+      case SILC_ID_CLIENT:
+       entry = (void *)silc_idlist_find_client_by_id(server->local_list, 
+                                                     id, NULL);
+       if (!entry && check_global)
+         entry = (void *)silc_idlist_find_client_by_id(server->global_list, 
+                                                       id, NULL);
+       if (entry) {
+         *clients = silc_realloc(*clients, sizeof(**clients) * 
+                                 (*clients_count + 1));
+         (*clients)[(*clients_count)++] = (SilcClientEntry)entry;
+       } else {
+         silc_server_command_send_status_data(cmd, SILC_COMMAND_IDENTIFY,
+                                       SILC_STATUS_ERR_NO_SUCH_CLIENT_ID,
+                                              2, tmp, len);
+         error = TRUE;
+       }
+
+       break;
+       
+      case SILC_ID_SERVER:
+       entry = (void *)silc_idlist_find_server_by_id(server->local_list, 
+                                                     id, NULL);
+       if (!entry && check_global)
+         entry = (void *)silc_idlist_find_server_by_id(server->global_list, 
+                                                       id, NULL);
+       if (entry) {
+         *servers = silc_realloc(*servers, sizeof(**servers) * 
+                                 (*servers_count + 1));
+         (*servers)[(*servers_count)++] = (SilcServerEntry)entry;
+       } else {
+         silc_server_command_send_status_data(cmd, SILC_COMMAND_IDENTIFY,
+                                       SILC_STATUS_ERR_NO_SUCH_SERVER_ID,
+                                              2, tmp, len);
+         error = TRUE;
+       }
+       break;
+       
+      case SILC_ID_CHANNEL:
+       entry = (void *)silc_idlist_find_channel_by_id(server->local_list, 
+                                                      id, NULL);
+       if (!entry && check_global)
+         entry = (void *)silc_idlist_find_channel_by_id(server->global_list, 
+                                                        id, NULL);
+       if (entry) {
+         *channels = silc_realloc(*channels, sizeof(**channels) * 
+                                  (*channels_count + 1));
+         (*channels)[(*channels_count)++] = (SilcChannelEntry)entry;
+       } else {
+         silc_server_command_send_status_data(cmd, SILC_COMMAND_IDENTIFY,
+                                       SILC_STATUS_ERR_NO_SUCH_CHANNEL_ID,
+                                              2, tmp, len);
+         error = TRUE;
+       }
+       break;
+      }
+
+      silc_free(id);
+    }
+  }
+
+  if (error) {
+    silc_free(*clients);
+    silc_free(*servers);
+    silc_free(*channels);
+    return FALSE;
+  }
+  
+  /* Get the max count of reply messages allowed */
+  tmp = silc_argument_get_arg_type(cmd->args, 4, NULL);
+  if (tmp)
+    *count = atoi(tmp);
+  else
+    *count = 0;
+
+  return TRUE;
+}
+
+/* Checks that all mandatory fields in client entry are present. If not
+   then send WHOIS request to the server who owns the client. We use
+   WHOIS because we want to get as much information as possible at once. */
 
 static char
-silc_server_command_identify_check(SilcServerCommandContext cmd,
-                                  SilcClientEntry *clients,
-                                  uint32 clients_count)
+silc_server_command_identify_check_client(SilcServerCommandContext cmd,
+                                         SilcClientEntry *clients,
+                                         uint32 clients_count)
 {
   SilcServer server = cmd->server;
   int i;
@@ -1401,121 +1460,218 @@ static void
 silc_server_command_identify_send_reply(SilcServerCommandContext cmd,
                                        SilcClientEntry *clients,
                                        uint32 clients_count,
+                                       SilcServerEntry *servers,
+                                       uint32 servers_count,
+                                       SilcChannelEntry *channels,
+                                       uint32 channels_count,
                                        int count)
 {
   SilcServer server = cmd->server;
-  char *tmp;
   int i, k, len;
   SilcBuffer packet, idp;
-  SilcClientEntry entry;
   SilcCommandStatus status;
   uint16 ident = silc_command_get_ident(cmd->payload);
   char nh[256], uh[256];
   SilcSocketConnection hsock;
 
-  len = 0;
-  for (i = 0; i < clients_count; i++)
-    if (clients[i]->data.registered)
-      len++;
+  status = SILC_STATUS_OK;
+
+  if (clients) {
+    SilcClientEntry entry;
+
+    len = 0;
+    for (i = 0; i < clients_count; i++)
+      if (clients[i]->data.registered)
+       len++;
+
+    if (len > 1)
+      status = SILC_STATUS_LIST_START;
+
+    for (i = 0, k = 0; i < clients_count; i++) {
+      entry = clients[i];
+      
+      if (entry->data.registered == FALSE) {
+       if (clients_count == 1) {
+         SilcBuffer idp = silc_id_payload_encode(entry->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);
+       }
+       continue;
+      }
+      
+      if (k >= 1)
+       status = SILC_STATUS_LIST_ITEM;
+      if (clients_count > 1 && k == clients_count - 1 
+         && !servers_count && !channels_count)
+       status = SILC_STATUS_LIST_END;
+      if (count && k - 1 == count)
+       status = SILC_STATUS_LIST_END;
+      if (count && k - 1 > count)
+       break;
+      
+      /* Send IDENTIFY reply */
+      idp = silc_id_payload_encode(entry->id, SILC_ID_CLIENT);
+      
+      memset(uh, 0, sizeof(uh));
+      memset(nh, 0, sizeof(nh));
+      
+      strncat(nh, entry->nickname, strlen(entry->nickname));
+      if (!strchr(entry->nickname, '@')) {
+       strncat(nh, "@", 1);
+       if (entry->servername) {
+         strncat(nh, entry->servername, strlen(entry->servername));
+       } else {
+         len = entry->router ? strlen(entry->router->server_name) :
+           strlen(server->server_name);
+         strncat(nh, entry->router ? entry->router->server_name :
+                 server->server_name, len);
+       }
+      }
+      
+      if (!entry->username) {
+       packet = silc_command_reply_payload_encode_va(SILC_COMMAND_IDENTIFY,
+                                                     status, ident, 2,
+                                                     2, idp->data, idp->len, 
+                                                     3, nh, strlen(nh));
+      } else {
+       strncat(uh, entry->username, strlen(entry->username));
+       if (!strchr(entry->username, '@')) {
+         strncat(uh, "@", 1);
+         hsock = (SilcSocketConnection)entry->connection;
+         len = strlen(hsock->hostname);
+         strncat(uh, hsock->hostname, len);
+       }
+       
+       packet = silc_command_reply_payload_encode_va(SILC_COMMAND_IDENTIFY,
+                                                     status, ident, 3,
+                                                     2, idp->data, idp->len, 
+                                                     3, nh, strlen(nh),
+                                                     4, uh, strlen(uh));
+      }
+      
+      silc_server_packet_send(server, cmd->sock, SILC_PACKET_COMMAND_REPLY,
+                             0, packet->data, packet->len, FALSE);
+      
+      silc_buffer_free(packet);
+      silc_buffer_free(idp);
+      
+      k++;
+    }
+  }
+
+  status = (status == SILC_STATUS_LIST_ITEM ? 
+           SILC_STATUS_LIST_ITEM : SILC_STATUS_OK);
 
-  status = SILC_STATUS_OK;
-  if (len > 1)
-    status = SILC_STATUS_LIST_START;
+  if (servers) {
+    SilcServerEntry entry;
 
-  for (i = 0, k = 0; i < clients_count; i++) {
-    entry = clients[i];
+    if (status == SILC_STATUS_OK && servers_count > 1)
+      status = SILC_STATUS_LIST_START;
 
-    if (entry->data.registered == FALSE) {
-      if (clients_count == 1) {
-       SilcBuffer idp = silc_id_payload_encode(entry->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);
+    for (i = 0, k = 0; i < servers_count; i++) {
+      entry = servers[i];
+      
+      if (k >= 1)
+       status = SILC_STATUS_LIST_ITEM;
+      if (servers_count > 1 && k == servers_count - 1 && !channels_count)
+       status = SILC_STATUS_LIST_END;
+      if (count && k - 1 == count)
+       status = SILC_STATUS_LIST_END;
+      if (count && k - 1 > count)
+       break;
+      
+      /* Send IDENTIFY reply */
+      idp = silc_id_payload_encode(entry->id, SILC_ID_SERVER);
+      if (entry->server_name) {
+       packet = 
+         silc_command_reply_payload_encode_va(SILC_COMMAND_IDENTIFY,
+                                              status, ident, 2,
+                                              2, idp->data, idp->len, 
+                                              3, entry->server_name, 
+                                              strlen(entry->server_name));
+      } else {
+       packet = silc_command_reply_payload_encode_va(SILC_COMMAND_IDENTIFY,
+                                                     status, ident, 1,
+                                                     2, idp->data, idp->len);
       }
-      continue;
+      
+      silc_server_packet_send(server, cmd->sock, SILC_PACKET_COMMAND_REPLY,
+                             0, packet->data, packet->len, FALSE);
+      
+      silc_buffer_free(packet);
+      silc_buffer_free(idp);
+      
+      k++;
     }
+  }
 
-    if (k >= 1)
-      status = SILC_STATUS_LIST_ITEM;
-
-    if (clients_count > 1 && k == clients_count - 1)
-      status = SILC_STATUS_LIST_END;
+  status = (status == SILC_STATUS_LIST_ITEM ? 
+           SILC_STATUS_LIST_ITEM : SILC_STATUS_OK);
 
-    if (count && k - 1 == count)
-      status = SILC_STATUS_LIST_END;
+  if (channels) {
+    SilcChannelEntry entry;
 
-    if (count && k - 1 > count)
-      break;
+    if (status == SILC_STATUS_OK && channels_count > 1)
+      status = SILC_STATUS_LIST_START;
 
-    /* Send IDENTIFY reply */
-    idp = silc_id_payload_encode(entry->id, SILC_ID_CLIENT);
-    tmp = silc_argument_get_first_arg(cmd->args, NULL);
-    
-    memset(uh, 0, sizeof(uh));
-    memset(nh, 0, sizeof(nh));
+    for (i = 0, k = 0; i < channels_count; i++) {
+      entry = channels[i];
       
-    strncat(nh, entry->nickname, strlen(entry->nickname));
-    if (!strchr(entry->nickname, '@')) {
-      strncat(nh, "@", 1);
-      if (entry->servername) {
-       strncat(nh, entry->servername, strlen(entry->servername));
+      if (k >= 1)
+       status = SILC_STATUS_LIST_ITEM;
+      if (channels_count > 1 && k == channels_count - 1)
+       status = SILC_STATUS_LIST_END;
+      if (count && k - 1 == count)
+       status = SILC_STATUS_LIST_END;
+      if (count && k - 1 > count)
+       break;
+      
+      /* Send IDENTIFY reply */
+      idp = silc_id_payload_encode(entry->id, SILC_ID_CHANNEL);
+      if (entry->channel_name) {
+       packet = 
+         silc_command_reply_payload_encode_va(SILC_COMMAND_IDENTIFY,
+                                              status, ident, 2,
+                                              2, idp->data, idp->len, 
+                                              3, entry->channel_name, 
+                                              strlen(entry->channel_name));
       } else {
-       len = entry->router ? strlen(entry->router->server_name) :
-         strlen(server->server_name);
-       strncat(nh, entry->router ? entry->router->server_name :
-               server->server_name, len);
+       packet = silc_command_reply_payload_encode_va(SILC_COMMAND_IDENTIFY,
+                                                     status, ident, 1,
+                                                     2, idp->data, idp->len);
       }
-    }
       
-    if (!entry->username) {
-      packet = silc_command_reply_payload_encode_va(SILC_COMMAND_IDENTIFY,
-                                                   status, ident, 2,
-                                                   2, idp->data, idp->len, 
-                                                   3, nh, strlen(nh));
-    } else {
-      strncat(uh, entry->username, strlen(entry->username));
-      if (!strchr(entry->username, '@')) {
-       strncat(uh, "@", 1);
-       hsock = (SilcSocketConnection)entry->connection;
-       len = strlen(hsock->hostname);
-       strncat(uh, hsock->hostname, len);
-      }
+      silc_server_packet_send(server, cmd->sock, SILC_PACKET_COMMAND_REPLY,
+                             0, packet->data, packet->len, FALSE);
       
-      packet = silc_command_reply_payload_encode_va(SILC_COMMAND_IDENTIFY,
-                                                   status, ident, 3,
-                                                   2, idp->data, idp->len, 
-                                                   3, nh, strlen(nh),
-                                                   4, uh, strlen(uh));
-    }
+      silc_buffer_free(packet);
+      silc_buffer_free(idp);
       
-    silc_server_packet_send(server, cmd->sock, SILC_PACKET_COMMAND_REPLY,
-                           0, packet->data, packet->len, FALSE);
-    
-    silc_buffer_free(packet);
-    silc_buffer_free(idp);
-
-    k++;
+      k++;
+    }
   }
 }
 
 static int
-silc_server_command_identify_from_client(SilcServerCommandContext cmd)
+silc_server_command_identify_process(SilcServerCommandContext cmd)
 {
   SilcServer server = cmd->server;
-  char *nick = NULL, *server_name = NULL;
-  int count = 0;
-  SilcClientEntry *clients = NULL, entry;
-  SilcClientID **client_id = NULL;
-  uint32 client_id_count = 0, clients_count = 0;
-  int i, ret = 0;
+  uint32 count = 0;
+  int ret = 0;
+  SilcClientEntry *clients = NULL;
+  SilcServerEntry *servers = NULL;
+  SilcChannelEntry *channels = NULL;
+  uint32 clients_count = 0, servers_count = 0, channels_count = 0;
 
   /* Protocol dictates that we must always send the received IDENTIFY request
      to our router if we are normal server, so let's do it now unless we
      are standalone. We will not send any replies to the client until we
      have received reply from the router. */
-  if (server->server_type == SILC_SERVER && 
-      !cmd->pending && !server->standalone) {
+  if (cmd->sock->type == SILC_SOCKET_TYPE_CLIENT && 
+      server->server_type == SILC_SERVER && !cmd->pending && 
+      !server->standalone) {
     SilcBuffer tmpbuf;
     uint16 old_ident;
 
@@ -1548,197 +1704,32 @@ silc_server_command_identify_from_client(SilcServerCommandContext cmd)
      requested client and send reply to the requesting client. */
 
   /* Parse the IDENTIFY request */
-  if (!silc_server_command_whois_parse(cmd, &client_id, &client_id_count,
-                                      &nick, &server_name, &count,
-                                      SILC_COMMAND_IDENTIFY))
+  if (!silc_server_command_identify_parse(cmd,
+                                         &clients, &clients_count,
+                                         &servers, &servers_count,
+                                         &channels, &channels_count,
+                                         &count))
     return 0;
 
-  /* Get all clients matching that ID or nickname from local list */
-  if (client_id_count) { 
-    /* Check all Client ID's received in the command packet */
-    for (i = 0; i < client_id_count; i++) {
-      entry = silc_idlist_find_client_by_id(server->local_list, 
-                                           client_id[i], NULL);
-      if (entry) {
-       clients = silc_realloc(clients, sizeof(*clients) * 
-                              (clients_count + 1));
-       clients[clients_count++] = entry;
-      }
-    }
-  } else {
-    if (!silc_idlist_get_clients_by_hash(server->local_list, 
-                                        nick, server->md5hash,
-                                        &clients, &clients_count))
-      silc_idlist_get_clients_by_nickname(server->local_list, 
-                                         nick, server_name,
-                                         &clients, &clients_count);
-  }
-  
-  /* Check global list as well */
-  if (client_id_count) {
-    /* Check all Client ID's received in the command packet */
-    for (i = 0; i < client_id_count; i++) {
-      entry = silc_idlist_find_client_by_id(server->global_list, 
-                                           client_id[i], NULL);
-      if (entry) {
-       clients = silc_realloc(clients, sizeof(*clients) * 
-                              (clients_count + 1));
-       clients[clients_count++] = entry;
-      }
-    }
-  } else {
-    if (!silc_idlist_get_clients_by_hash(server->global_list, 
-                                        nick, server->md5hash,
-                                        &clients, &clients_count))
-      silc_idlist_get_clients_by_nickname(server->global_list, 
-                                         nick, server_name,
-                                         &clients, &clients_count);
-  }
-  
-  if (!clients) {
-    /* Such a client really does not exist in the SILC network. */
-    if (!client_id_count) {
-      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[0], 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;
-  }
-
   /* Check that all mandatory fields are present and request those data
      from the server who owns the client if necessary. */
-  if (!silc_server_command_identify_check(cmd, clients, clients_count)) {
+  if (clients && !silc_server_command_identify_check_client(cmd, clients, 
+                                                           clients_count)) {
     ret = -1;
     goto out;
   }
 
   /* Send the command reply to the client */
-  silc_server_command_identify_send_reply(cmd, clients, clients_count,
+  silc_server_command_identify_send_reply(cmd, 
+                                         clients, clients_count,
+                                         servers, servers_count,
+                                         channels, channels_count, 
                                          count);
 
  out:
-  if (client_id_count) {
-    for (i = 0; i < client_id_count; i++)
-      silc_free(client_id[i]);
-    silc_free(client_id);
-  }
-  if (clients)
-    silc_free(clients);
-  if (nick)
-    silc_free(nick);
-  if (server_name)
-    silc_free(server_name);
-
-  return ret;
-}
-
-static int
-silc_server_command_identify_from_server(SilcServerCommandContext cmd)
-{
-  SilcServer server = cmd->server;
-  char *nick = NULL, *server_name = NULL;
-  int count = 0;
-  SilcClientEntry *clients = NULL, entry;
-  SilcClientID **client_id = NULL;
-  uint32 client_id_count = 0, clients_count = 0;
-  int i, ret = 0;
-
-  /* Parse the IDENTIFY request */
-  if (!silc_server_command_whois_parse(cmd, &client_id, &client_id_count,
-                                      &nick, &server_name, &count,
-                                      SILC_COMMAND_IDENTIFY))
-    return 0;
-
-  /* Process the command request. Let's search for the requested client and
-     send reply to the requesting server. */
-
-  if (client_id_count) {
-    /* Check all Client ID's received in the command packet */
-    for (i = 0; i < client_id_count; i++) {
-      entry = silc_idlist_find_client_by_id(server->local_list, 
-                                           client_id[i], NULL);
-      if (entry) {
-       clients = silc_realloc(clients, sizeof(*clients) * 
-                              (clients_count + 1));
-       clients[clients_count++] = entry;
-      }
-    }
-  } else {
-    if (!silc_idlist_get_clients_by_hash(server->local_list, 
-                                        nick, server->md5hash,
-                                        &clients, &clients_count))
-      silc_idlist_get_clients_by_nickname(server->local_list, 
-                                         nick, server_name,
-                                         &clients, &clients_count);
-  }
-  
-  /* If we are router we will check our global list as well. */
-  if (server->server_type == SILC_ROUTER) {
-    if (client_id_count) {
-      /* Check all Client ID's received in the command packet */
-      for (i = 0; i < client_id_count; i++) {
-       entry = silc_idlist_find_client_by_id(server->global_list, 
-                                             client_id[i], NULL);
-       if (entry) {
-         clients = silc_realloc(clients, sizeof(*clients) * 
-                                (clients_count + 1));
-         clients[clients_count++] = entry;
-       }
-      }
-    } else {
-      if (!silc_idlist_get_clients_by_hash(server->global_list, 
-                                          nick, server->md5hash,
-                                          &clients, &clients_count))
-       silc_idlist_get_clients_by_nickname(server->global_list, 
-                                           nick, server_name,
-                                           &clients, &clients_count);
-    }
-  }
-
-  if (!clients) {
-    /* Such a client really does not exist in the SILC network. */
-    if (!client_id_count) {
-      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[0], 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;
-  }
-
-  /* Check that all mandatory fields are present and request those data
-     from the server who owns the client if necessary. */
-  if (!silc_server_command_identify_check(cmd, clients, clients_count)) {
-    ret = -1;
-    goto out;
-  }
-
-  /* Send the command reply */
-  silc_server_command_identify_send_reply(cmd, clients, clients_count, count);
-
- out:
-  if (client_id_count) {
-    for (i = 0; i < client_id_count; i++)
-      silc_free(client_id[i]);
-    silc_free(client_id);
-  }
-  if (clients)
-    silc_free(clients);
-  if (nick)
-    silc_free(nick);
-  if (server_name)
-    silc_free(server_name);
+  silc_free(clients);
+  silc_free(servers);
+  silc_free(channels);
 
   return ret;
 }
@@ -1750,11 +1741,7 @@ SILC_SERVER_CMD_FUNC(identify)
 
   SILC_SERVER_COMMAND_CHECK(SILC_COMMAND_IDENTIFY, cmd, 1, 3328);
 
-  if (cmd->sock->type == SILC_SOCKET_TYPE_CLIENT)
-    ret = silc_server_command_identify_from_client(cmd);
-  else if ((cmd->sock->type == SILC_SOCKET_TYPE_SERVER) |
-          (cmd->sock->type == SILC_SOCKET_TYPE_ROUTER))
-    ret = silc_server_command_identify_from_server(cmd);
+  ret = silc_server_command_identify_process(cmd);
 
   if (!ret)
     silc_server_command_free(cmd);
@@ -4741,8 +4728,8 @@ SILC_SERVER_CMD_FUNC(users)
   SilcServerCommandContext cmd = (SilcServerCommandContext)context;
   SilcServer server = cmd->server;
   SilcChannelEntry channel;
-  SilcChannelID *id;
-  SilcBuffer packet;
+  SilcChannelID *id = NULL;
+  SilcBuffer packet, idp;
   unsigned char *channel_id;
   uint32 channel_id_len;
   SilcBuffer client_id_list;
@@ -4750,27 +4737,40 @@ SILC_SERVER_CMD_FUNC(users)
   unsigned char lc[4];
   uint32 list_count = 0;
   uint16 ident = silc_command_get_ident(cmd->payload);
+  char *channel_name;
 
-  SILC_SERVER_COMMAND_CHECK(SILC_COMMAND_USERS, cmd, 1, 1);
+  SILC_SERVER_COMMAND_CHECK(SILC_COMMAND_USERS, cmd, 1, 2);
 
   /* Get Channel ID */
   channel_id = silc_argument_get_arg_type(cmd->args, 1, &channel_id_len);
-  if (!channel_id) {
+
+  /* Get channel name */
+  channel_name = silc_argument_get_arg_type(cmd->args, 2, NULL);
+
+  if (!channel_id && !channel_name) {
     silc_server_command_send_status_reply(cmd, SILC_COMMAND_USERS,
                                          SILC_STATUS_ERR_NO_CHANNEL_ID);
     goto out;
   }
-  id = silc_id_payload_parse_id(channel_id, channel_id_len);
-  if (!id) {
-    silc_server_command_send_status_reply(cmd, SILC_COMMAND_USERS,
-                                         SILC_STATUS_ERR_NO_CHANNEL_ID);
-    goto out;
+
+  if (channel_id) {
+    id = silc_id_payload_parse_id(channel_id, channel_id_len);
+    if (!id) {
+      silc_server_command_send_status_reply(cmd, SILC_COMMAND_USERS,
+                                           SILC_STATUS_ERR_NO_CHANNEL_ID);
+      goto out;
+    }
   }
 
   /* If we are server and we don't know about this channel we will send
      the command to our router. If we know about the channel then we also
      have the list of users already. */
-  channel = silc_idlist_find_channel_by_id(server->local_list, id, NULL);
+  if (id)
+    channel = silc_idlist_find_channel_by_id(server->local_list, id, NULL);
+  else
+    channel = silc_idlist_find_channel_by_name(server->local_list, 
+                                              channel_name, NULL);
+
   if (!channel) {
     if (server->server_type == SILC_SERVER && !server->standalone &&
        !cmd->pending) {
@@ -4799,7 +4799,11 @@ SILC_SERVER_CMD_FUNC(users)
     }
 
     /* We are router and we will check the global list as well. */
-    channel = silc_idlist_find_channel_by_id(server->global_list, id, NULL);
+    if (id)
+      channel = silc_idlist_find_channel_by_id(server->global_list, id, NULL);
+    else
+      channel = silc_idlist_find_channel_by_name(server->local_list, 
+                                                channel_name, NULL);
     if (!channel) {
       /* Channel really does not exist */
       silc_server_command_send_status_reply(cmd, SILC_COMMAND_USERS,
@@ -4808,6 +4812,13 @@ SILC_SERVER_CMD_FUNC(users)
     }
   }
 
+  /* If the channel is private or secret do not send anything */
+  if (channel->mode & (SILC_CHANNEL_MODE_PRIVATE | SILC_CHANNEL_MODE_SECRET)) {
+    silc_server_command_send_status_reply(cmd, SILC_COMMAND_USERS,
+                                         SILC_STATUS_ERR_NO_SUCH_CHANNEL);
+    goto out;
+  }
+
   /* Get the users list */
   silc_server_get_users_on_channel(server, channel, &client_id_list,
                                   &client_mode_list, &list_count);
@@ -4816,9 +4827,10 @@ SILC_SERVER_CMD_FUNC(users)
   SILC_PUT32_MSB(list_count, lc);
 
   /* Send reply */
+  idp = silc_id_payload_encode(channel->id, SILC_ID_CHANNEL);
   packet = silc_command_reply_payload_encode_va(SILC_COMMAND_USERS,
                                                SILC_STATUS_OK, ident, 4,
-                                               2, channel_id, channel_id_len,
+                                               2, idp->data, idp->len,
                                                3, lc, 4,
                                                4, client_id_list->data,
                                                client_id_list->len,
@@ -4827,10 +4839,12 @@ SILC_SERVER_CMD_FUNC(users)
   silc_server_packet_send(server, cmd->sock, SILC_PACKET_COMMAND_REPLY, 0, 
                          packet->data, packet->len, FALSE);
     
+  silc_buffer_free(idp);
   silc_buffer_free(packet);
   silc_buffer_free(client_id_list);
   silc_buffer_free(client_mode_list);
-  silc_free(id);
+  if (id)
+    silc_free(id);
 
  out:
   silc_server_command_free(cmd);
index a012aa8c7db56bbf843e87b706c5b60de4028f7c..557a857fa68b953a590912a2365e3412ea8abb55 100644 (file)
@@ -404,94 +404,182 @@ silc_server_command_reply_identify_save(SilcServerCommandReplyContext cmd)
   SilcServer server = cmd->server;
   uint32 len, id_len;
   unsigned char *id_data;
-  char *nickname, *username;
-  SilcClientID *client_id;
+  char *name, *info;
+  SilcClientID *client_id = NULL;
+  SilcServerID *server_id = NULL;
+  SilcChannelID *channel_id = NULL;
   SilcClientEntry client;
+  SilcServerEntry server_entry;
+  SilcChannelEntry channel;
   char global = FALSE;
   char *nick = NULL;
+  SilcIDPayload idp = NULL;
+  SilcIdType id_type;
 
   id_data = silc_argument_get_arg_type(cmd->args, 2, &id_len);
-  nickname = silc_argument_get_arg_type(cmd->args, 3, &len);
-  username = silc_argument_get_arg_type(cmd->args, 4, &len);
   if (!id_data)
     return FALSE;
-
-  client_id = silc_id_payload_parse_id(id_data, id_len);
-  if (!client_id)
+  idp = silc_id_payload_parse_data(id_data, id_len);
+  if (!idp)
     return FALSE;
 
-  /* Check if we have this client cached already. */
+  name = silc_argument_get_arg_type(cmd->args, 3, &len);
+  info = silc_argument_get_arg_type(cmd->args, 4, &len);
 
-  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);
-    global = TRUE;
-  }
+  id_type = silc_id_payload_get_type(idp);
 
-  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)
-      return FALSE;
+  switch (id_type) {
+  case SILC_ID_CLIENT:
+    client_id = silc_id_payload_get_id(idp);
+    if (!client_id)
+      goto error;
 
-    /* Take hostname out of nick string if it includes it. */
-    if (nickname) {
-      if (strchr(nickname, '@')) {
-       int len = strcspn(nickname, "@");
-       nick = silc_calloc(len + 1, sizeof(char));
-       memcpy(nick, nickname, len);
-      } else {
-       nick = strdup(nickname);
+    SILC_LOG_DEBUG(("Received client information"));
+
+    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);
+      global = TRUE;
+    }
+    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 error;
+      
+      /* Take hostname out of nick string if it includes it. */
+      if (name) {
+       if (strchr(name, '@')) {
+         int len = strcspn(name, "@");
+         nick = silc_calloc(len + 1, sizeof(char));
+         memcpy(nick, name, len);
+       } else {
+         nick = strdup(name);
+       }
       }
+
+      /* 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, nick, 
+                                     info ? strdup(info) : NULL, NULL,
+                                     client_id, cmd->sock->user_data, NULL);
+      client->data.registered = TRUE;
+    } else {
+      /* We have the client already, update the data */
+      
+      SILC_LOG_DEBUG(("Updating client data"));
+      
+      /* Take hostname out of nick string if it includes it. */
+      if (name) {
+       if (strchr(name, '@')) {
+         int len = strcspn(name, "@");
+         nick = silc_calloc(len + 1, sizeof(char));
+         memcpy(nick, name, len);
+       } else {
+         nick = strdup(name);
+       }
+      }
+      
+      if (name && client->nickname)
+       silc_free(client->nickname);
+      
+      if (nick)
+       client->nickname = nick;
+      
+      if (info && client->username) {
+       silc_free(client->username);
+       client->username = strdup(info);
+      }
+      
+      /* Remove the old cache entry and create a new one */
+      if (name) {
+       silc_idcache_del_by_context(global ? server->global_list->clients :
+                                   server->local_list->clients, client);
+       silc_idcache_add(global ? server->global_list->clients :
+                        server->local_list->clients, nick, client->id, 
+                        client, FALSE);
+      }
+
+      silc_free(client_id);
     }
 
-    /* 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, nick, 
-                                   username ? strdup(username) : NULL, NULL,
-                                   client_id, cmd->sock->user_data, NULL);
-    client->data.registered = TRUE;
-  } else {
-    /* We have the client already, update the data */
+    break;
 
-    SILC_LOG_DEBUG(("Updating client data"));
+  case SILC_ID_SERVER:
+    server_id = silc_id_payload_get_id(idp);
+    if (!server_id)
+      goto error;
 
-    /* Take hostname out of nick string if it includes it. */
-    if (nickname) {
-      if (strchr(nickname, '@')) {
-       int len = strcspn(nickname, "@");
-       nick = silc_calloc(len + 1, sizeof(char));
-       memcpy(nick, nickname, len);
-      } else {
-       nick = strdup(nickname);
+    SILC_LOG_DEBUG(("Received server information"));
+
+    server_entry = silc_idlist_find_server_by_id(server->local_list, 
+                                                server_id, NULL);
+    if (!server_entry)
+      server_entry = silc_idlist_find_server_by_id(server->global_list, 
+                                                  server_id, NULL);
+    if (!server_entry) {
+      /* If router did not find such Server 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 error;
+      
+      /* We don't have that server anywhere, add it. */
+      server_entry = silc_idlist_add_server(server->global_list, 
+                                           strdup(name), 0,
+                                           server_id, NULL, NULL);
+      if (!server_entry) {
+       silc_free(server_id);
+       goto error;
       }
+      server_id = NULL;
     }
 
-    if (nickname && client->nickname)
-      silc_free(client->nickname);
+    silc_free(server_id);
+    break;
 
-    if (nickname)
-      client->nickname = nick;
+  case SILC_ID_CHANNEL:
+    channel_id = silc_id_payload_get_id(idp);
+    if (!channel_id)
+      goto error;
 
-    if (username && client->username) {
-      silc_free(client->username);
-      client->username = strdup(username);
-    }
+    SILC_LOG_DEBUG(("Received channel information"));
 
-    /* Remove the old cache entry and create a new one */
-    if (nickname) {
-      silc_idcache_del_by_context(global ? server->global_list->clients :
-                                 server->local_list->clients, client);
-      silc_idcache_add(global ? server->global_list->clients :
-                      server->local_list->clients, nick, client->id, 
-                      client, FALSE);
+    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) {
+      /* If router did not find such Server 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 error;
+      
+      /* We don't have that server anywhere, add it. */
+      channel = silc_idlist_add_channel(server->global_list, strdup(name),
+                                       SILC_CHANNEL_MODE_NONE, channel_id, 
+                                       server->router->connection, 
+                                       NULL, NULL);
+      if (!channel) {
+       silc_free(channel_id);
+       goto error;
+      }
+      channel_id = NULL;
     }
-    silc_free(client_id);
+
+    silc_free(channel_id);
+    break;
   }
 
+  silc_id_payload_free(idp);
   return TRUE;
+
+ error:
+  silc_id_payload_free(idp);
+  return FALSE;
 }
 
 /* Received reply for forwarded IDENTIFY command. We have received the
@@ -855,8 +943,26 @@ SILC_SERVER_CMD_REPLY_FUNC(users)
   if (!channel) {
     channel = silc_idlist_find_channel_by_id(server->global_list, 
                                             channel_id, NULL);
-    if (!channel)
-      goto out;
+    if (!channel) {
+      SilcBuffer idp;
+
+      if (server->server_type == SILC_ROUTER)
+       goto out;
+
+      idp = silc_id_payload_encode(channel_id, SILC_ID_CHANNEL);
+      silc_server_send_command(server, server->router->connection,
+                              SILC_COMMAND_IDENTIFY, ++server->cmd_ident,
+                              1, 5, idp->data, idp->len);
+      silc_buffer_free(idp);
+
+      /* Register pending command callback. After we've received the channel
+        information we will reprocess this command reply by re-calling this
+        USERS command reply callback. */
+      silc_server_command_pending(server, SILC_COMMAND_IDENTIFY, 
+                                 server->cmd_ident,
+                                 NULL, silc_server_command_reply_users, cmd);
+      return;
+    }
   }
 
   /* Get the list count */
index 4e5f5272a8c7305dcd7bfc7b2936b7a566d9d3de..0d7ed54d5235f49f241d30b914e29f0e25eec442 100644 (file)
@@ -558,6 +558,8 @@ silc_idlist_add_channel(SilcIDList id_list, char *channel_name, int mode,
 {
   SilcChannelEntry channel;
 
+  SILC_LOG_DEBUG(("Adding new channel entry"));
+
   channel = silc_calloc(1, sizeof(*channel));
   channel->channel_name = channel_name;
   channel->mode = mode;
index 7ac6826faf0ad46138e6ad46e6bf09324f41ed71..be1ee659804362d3b35ec4024f44c46295d076ee 100644 (file)
@@ -1864,7 +1864,7 @@ void silc_server_new_channel(SilcServer server,
   id = silc_channel_get_id(payload, &id_len);
 
   if (sock->type == SILC_SOCKET_TYPE_ROUTER) {
-    /* Add the server to global list as it is coming from router. It 
+    /* Add the channel to global list as it is coming from router. It 
        cannot be our own channel as it is coming from router. */
 
     SILC_LOG_DEBUG(("New channel id(%s) from [Router] %s",
index c550fbae4e585279967c35f51a3e77be5bdc38cc..31e9bea15e373b42dc9d9e38565d14d0ecfb469d 100644 (file)
@@ -1573,6 +1573,7 @@ void silc_server_send_channel_key(SilcServer server,
 void silc_server_send_command(SilcServer server, 
                              SilcSocketConnection sock,
                              SilcCommand command, 
+                             uint16 ident,
                              uint32 argc, ...)
 {
   SilcBuffer packet;
@@ -1580,7 +1581,7 @@ void silc_server_send_command(SilcServer server,
 
   va_start(ap, argc);
 
-  packet = silc_command_payload_encode_vap(command, 0, argc, ap);
+  packet = silc_command_payload_encode_vap(command, ident, argc, ap);
   silc_server_packet_send(server, sock, SILC_PACKET_COMMAND, 0,
                          packet->data, packet->len, TRUE);
   silc_buffer_free(packet);
index 5a18523329e62e1455e46fa1e67708806f2d8075..67f54c0d23abb96745ffad965457b264c19cb0f1 100644 (file)
@@ -213,6 +213,7 @@ void silc_server_send_channel_key(SilcServer server,
 void silc_server_send_command(SilcServer server, 
                              SilcSocketConnection sock,
                              SilcCommand command, 
+                             uint16 ident,
                              uint32 argc, ...);
 void silc_server_send_heartbeat(SilcServer server,
                                SilcSocketConnection sock);
index c03fd1929df64c4ce8176015dae0d4273df19aeb..1b538c85bac0a116740ea795e4f483a028363c9c 100644 (file)
@@ -234,15 +234,15 @@ List of all defined commands in SILC follows.
         nicknames in the SILC.  The <count> option may be given to narrow
         down the number of accepted results.  If this is not defined there
         are no limit of accepted results.  The query may also be narrowed
-        down by defining the server name of the nickname.
+        down by defining the server name of the nickname.  The <count> is
+        int string format.
 
         It is also possible to search the user by Client ID.  If the 
         <Client ID> is provided server MUST use it as the search value
         instead of the <nickname>.  One of the arguments MUST be given.
         It is also possible to define multiple Client ID's to search
         multiple users sending only one WHOIS command.  In this case the
-        Client ID's are appended as normal arguments.  The server replies
-        in this case with only one reply message for all requested users.
+        Client ID's are appended as normal arguments.
 
         To prevent miss-use of this command wildcards in the nickname
         or in the server name are not permitted.  It is not allowed
@@ -311,7 +311,7 @@ List of all defined commands in SILC follows.
         given to narrow down the number of accepted results.  If this
         is not defined there are no limit of accepted results.  The query
         may also be narrowed down by defining the server name of the 
-        nickname.
+        nickname.  The <count> is in string format.
 
         To prevent miss-use of this command wildcards in the nickname
         or in the server name are not permitted.  The WHOWAS requests MUST 
@@ -352,49 +352,45 @@ List of all defined commands in SILC follows.
    3    SILC_COMMAND_IDENTIFY
 
         Max Arguments:  3328
-            Arguments:  (1) [<nickname>[@<server>]]  (2) [<count>]
-                        (3) [<Client ID>]            (n) [...]
-
-        Identify.  Identify command is almost analogous to WHOIS command,
-        except that it does not return as much information.  Only relevant
-        information such as Client ID is returned.  This is usually used
-        to get the Client ID of a client used in the communication with
-        the client.
-
-        The query may find multiple matching users as there are no unique 
-        nicknames in the SILC.  The <count> option may be given to narrow 
-        down the number of accepted results.  If this is not defined there 
-        are no limit of accepted results.  The query may also be narrowed 
-        down by defining the server name of the nickname.
-
-        It is also possible to search the user by Client ID.  If the
-        <Client ID> is provided server must use it as the search value
-        instead of the <nickname>.  One of the arguments must be given.
-        It is also possible to define multiple Client ID's to search
-        multiple users sending only one IDENTIFY command.  In this case
-        the Client ID's are appended as normal arguments.  The server
-        replies in this case with only one reply message for all requested
-        users.
-
-        To prevent miss-use of this command wildcards in the nickname
-        or in the server name are not permitted.  It is not allowed
-        to request all users on some server.  The IDENTIFY requests MUST
-        be based on specific nickname request.
+            Arguments:  (1) [<nickname>[@<server>]]  (2) [<server name>]
+                        (3) [<channel name>]         (4) [<count>]
+                        (5) [<ID Payload>]           (n) [...]
+
+        Identify command is used to query information about an entity by
+        the entity's name or ID.  This command can be used to query
+        information about clients, server and channels.
+
+        The query may find multiple matching entities.  The <count> option
+        may be given to narrow down the number of accepted results.  If
+        this is not defined there are no limit of accepted results.  The
+        <count> is in string format.
+
+        It is also possible to search the entity by its ID.  If the
+        <ID Payload> is provided server must use it as the search value
+        instead of the entity's name.  One of the arguments must be given.
+        It is also possible to define multiple ID Payloads to search
+        multiple entities sending only one IDENTIFY command.  In this case
+        the ID Payloads are appended as normal arguments.  The type of the
+        entity is defined by the type of the ID Payload.
+
+        To prevent miss-use of this command wildcards in the names are
+        not permitted.  It is not allowed to request for example all users
+        on server.
 
         Implementations may not want to give interface access to this
-        command as it is hardly a command that would be used by an end user.
-        However, it must be implemented as it is used with private message
-        sending.
+        command as it is hardly a command that would be used by an end
+        user.  However, it must be implemented as it is used with private
+        message sending.
 
-        The IDENTIFY MUST be always sent to the router by server so that
-        all users are searched.  However, server MUST still search its
-        locally connected clients.
+        The IDENTIFY command MUST be always sent to the router by server
+        so that all users are searched.  However, server MUST still search
+        its locally connected clients.
 
         Reply messages to the command:
 
         Max Arguments:  4
-            Arguments:  (1) <Status Payload>         (2) <Client ID>
-                        (3) [<nickname>[@<server>]]  (4) [<username@host>]
+            Arguments:  (1) <Status Payload>   (2) <Client ID>
+                        (3) [<entity's name>]  (4) [<info>]
 
         This command may reply with several command reply messages to form
         a list of results.  In this case the status payload will include
@@ -402,10 +398,19 @@ List of all defined commands in SILC follows.
         the last reply to indicate the end of the list.  If there are only 
         one reply the status is set to normal STATUS_OK.
 
-        The command replies with Client ID of the nickname and if more
-        information is available it MAY reply with nickname and user name
-        and host name.  If the <count> option were defined in the query
-        there will be only <count> many replies from the server.
+        When querying clients the <entity's name> must include the client's
+        nickname in the following format: nickname>[@server].  The
+        <info> must include the client's username and host in the following
+        format: username@host.
+
+        When querying servers the <entity's name> must include the server's
+        full name.  The <info> may be omitted.
+
+        When querying channels the <entity's name> must include the
+        channel's name.  The <info> may be omitted.
+
+        If the <count> option were defined in the query there will be only
+        <count> many replies from the server.
 
         Status messages:
 
@@ -413,7 +418,11 @@ 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_SERVER
+            SILC_STATUS_ERR_NO_SUCH_CHANNEL
             SILC_STATUS_ERR_NO_SUCH_CLIENT_ID
+            SILC_STATUS_ERR_NO_SUCH_SERVER_ID
+            SILC_STATUS_ERR_NO_SUCH_CHANNEL_ID
             SILC_STATUS_ERR_WILDCARDS
             SILC_STATUS_ERR_NOT_ENOUGH_PARAMS
             SILC_STATUS_ERR_TOO_MANY_PARAMS
@@ -667,7 +676,8 @@ List of all defined commands in SILC follows.
         the requested server.
 
         If the <Server ID> is specified the server information if fetched
-        by the provided Server ID.
+        by the provided Server ID.  One of the arguments must always be
+        present.
 
         Reply messages to the command:
 
@@ -1464,12 +1474,13 @@ List of all defined commands in SILC follows.
 
    25   SILC_COMMAND_USERS
 
-        Max Arguments:  1
-            Arguments:  (1) <Channel ID>
+        Max Arguments:  2
+            Arguments:  (1) [<Channel ID>]  (2) [<channel name>]
 
         This command is used to list user names currently on the requested
-        channel; argument <Channel ID>.  The server MUST resolve the
-        user names and send a comma (`,') separated list of user names
+        channel; either the argument <Channel ID> or the <channel name>. 
+        One of these arguments must be present.  The server MUST resolve
+        the user names and send a comma (`,') separated list of user names
         on the channel.  Server or router MAY resolve the names by sending
         SILC_COMMAND_WHOIS or SILC_COMMAND_IDENTIFY commands.
 
index 2f857a1fcfc69535881a797c7d7c481c909fe104..b96905008976eb378b3e6f0fa3f902148e3b767e 100644 (file)
@@ -40,7 +40,9 @@ static void silc_client_packet_parse_type(SilcClient client,
    the client. The `application' is application specific user data pointer
    and caller must free it. */
 
-SilcClient silc_client_alloc(SilcClientOperations *ops, void *application,
+SilcClient silc_client_alloc(SilcClientOperations *ops, 
+                            SilcClientParams *params,
+                            void *application,
                             const char *silc_version)
 {
   SilcClient new_client;
@@ -49,6 +51,13 @@ SilcClient silc_client_alloc(SilcClientOperations *ops, void *application,
   new_client->application = application;
   new_client->ops = ops;
   new_client->silc_client_version = strdup(silc_version);
+  new_client->params = silc_calloc(1, sizeof(*new_client->params));
+
+  if (params)
+    memcpy(new_client->params, params, sizeof(*params));
+
+  if (!new_client->params->rekey_secs)
+    new_client->params->rekey_secs = 3600;
 
   return new_client;
 }
@@ -61,6 +70,8 @@ void silc_client_free(SilcClient client)
     if (client->rng)
       silc_rng_free(client->rng);
 
+    silc_free(client->silc_client_version);
+    silc_free(client->params);
     silc_free(client);
   }
 }
@@ -556,7 +567,7 @@ SILC_TASK_CALLBACK(silc_client_connect_to_server_final)
   conn->remote_id_data_len = silc_id_get_len(ctx->dest_id, SILC_ID_SERVER);
 
   /* Register re-key timeout */
-  conn->rekey->timeout = 3600; /* XXX hardcoded */
+  conn->rekey->timeout = client->params->rekey_secs;
   conn->rekey->context = (void *)client;
   silc_task_register(client->timeout_queue, conn->sock->sock, 
                     silc_client_rekey_callback,
@@ -1315,7 +1326,8 @@ void silc_client_receive_new_id(SilcClient client,
 
 /* Processed received Channel ID for a channel. This is called when client
    joins to channel and server replies with channel ID. The ID is cached. 
-   Returns the created channel entry. */
+   Returns the created channel entry. This is also called when received
+   channel ID in for example USERS command reply that we do not have. */
 
 SilcChannelEntry silc_client_new_channel_id(SilcClient client,
                                            SilcSocketConnection sock,
@@ -1334,8 +1346,6 @@ SilcChannelEntry silc_client_new_channel_id(SilcClient client,
   channel->mode = mode;
   silc_list_init(channel->clients, struct SilcChannelUserStruct, next);
 
-  conn->current_channel = channel;
-
   /* Put it to the ID cache */
   silc_idcache_add(conn->channel_cache, channel_name, (void *)channel->id, 
                   (void *)channel, FALSE);
index 1a3b8565ec79f32e7d88932e53095d3041eaad70..f886e3d374fe5064e67c5661f0d570b46acb3876 100644 (file)
@@ -158,6 +158,9 @@ struct SilcClientStruct {
   /* All client operations that are implemented in the application. */
   SilcClientOperations *ops;
 
+  /* Client Parameters */
+  SilcClientParams *params;
+
   /* SILC client scheduler and task queues */
   SilcSchedule schedule;
   SilcTaskQueue io_queue;
index 3e6839fad2fb0fe9a206909c22d3c7e032acade2..cb9519375a955209e904bd869d6ff5b7928b1e1d 100644 (file)
@@ -336,17 +336,23 @@ SILC_CLIENT_CMD_FUNC(identify)
     goto out;
   }
 
-  buffer = silc_command_payload_encode(SILC_COMMAND_IDENTIFY,
-                                      cmd->argc - 1, ++cmd->argv,
-                                      ++cmd->argv_lens, ++cmd->argv_types,
-                                      ++conn->cmd_ident);
+  if (cmd->argc == 2)
+    buffer = silc_command_payload_encode_va(SILC_COMMAND_IDENTIFY, 
+                                           ++conn->cmd_ident, 1,
+                                           1, cmd->argv[1],
+                                           cmd->argv_lens[1]);
+  else
+    buffer = silc_command_payload_encode_va(SILC_COMMAND_IDENTIFY, 
+                                           ++conn->cmd_ident, 2,
+                                           1, cmd->argv[1],
+                                           cmd->argv_lens[1],
+                                           4, cmd->argv[2],
+                                           cmd->argv_lens[2]);
+
   silc_client_packet_send(cmd->client, cmd->conn->sock,
                          SILC_PACKET_COMMAND, NULL, 0, NULL, NULL,
                          buffer->data, buffer->len, TRUE);
   silc_buffer_free(buffer);
-  cmd->argv--;
-  cmd->argv_lens--;
-  cmd->argv_types--;
 
   /* Notify application */
   COMMAND;
@@ -371,7 +377,8 @@ SILC_CLIENT_CMD_FUNC(nick)
   }
 
   if (cmd->argc < 2) {
-    cmd->client->ops->say(cmd->client, conn, SILC_CLIENT_MESSAGE_INFO, "Usage: /NICK <nickname>");
+    cmd->client->ops->say(cmd->client, conn, SILC_CLIENT_MESSAGE_INFO, 
+                         "Usage: /NICK <nickname>");
     COMMAND_ERROR;
     goto out;
   }
@@ -1975,14 +1982,16 @@ SILC_CLIENT_CMD_FUNC(leave)
   }
 
   if (cmd->argc != 2) {
-    cmd->client->ops->say(cmd->client, conn, SILC_CLIENT_MESSAGE_INFO, "Usage: /LEAVE <channel>");
+    cmd->client->ops->say(cmd->client, conn, SILC_CLIENT_MESSAGE_INFO, 
+                         "Usage: /LEAVE <channel>");
     COMMAND_ERROR;
     goto out;
   }
 
   if (cmd->argv[1][0] == '*') {
     if (!conn->current_channel) {
-      cmd->client->ops->say(cmd->client, conn, SILC_CLIENT_MESSAGE_INFO, "You are not on any channel");
+      cmd->client->ops->say(cmd->client, conn, SILC_CLIENT_MESSAGE_INFO, 
+                           "You are not on any channel");
       COMMAND_ERROR;
       goto out;
     }
@@ -1993,7 +2002,8 @@ SILC_CLIENT_CMD_FUNC(leave)
 
   /* Get the Channel ID of the channel */
   if (!silc_idcache_find_by_name_one(conn->channel_cache, name, &id_cache)) {
-    cmd->client->ops->say(cmd->client, conn, SILC_CLIENT_MESSAGE_INFO, "You are not on that channel");
+    cmd->client->ops->say(cmd->client, conn, SILC_CLIENT_MESSAGE_INFO, 
+                         "You are not on that channel");
     COMMAND_ERROR;
     goto out;
   }
@@ -2033,9 +2043,7 @@ SILC_CLIENT_CMD_FUNC(users)
 {
   SilcClientCommandContext cmd = (SilcClientCommandContext)context;
   SilcClientConnection conn = cmd->conn;
-  SilcIDCacheEntry id_cache = NULL;
-  SilcChannelEntry channel;
-  SilcBuffer buffer, idp;
+  SilcBuffer buffer;
   char *name;
 
   if (!cmd->conn) {
@@ -2045,14 +2053,16 @@ SILC_CLIENT_CMD_FUNC(users)
   }
 
   if (cmd->argc != 2) {
-    cmd->client->ops->say(cmd->client, conn, SILC_CLIENT_MESSAGE_INFO, "Usage: /USERS <channel>");
+    cmd->client->ops->say(cmd->client, conn, SILC_CLIENT_MESSAGE_INFO, 
+                         "Usage: /USERS <channel>");
     COMMAND_ERROR;
     goto out;
   }
 
   if (cmd->argv[1][0] == '*') {
     if (!conn->current_channel) {
-      cmd->client->ops->say(cmd->client, conn, SILC_CLIENT_MESSAGE_INFO, "You are not on any channel");
+      cmd->client->ops->say(cmd->client, conn, SILC_CLIENT_MESSAGE_INFO, 
+                           "You are not on any channel");
       COMMAND_ERROR;
       goto out;
     }
@@ -2061,45 +2071,14 @@ SILC_CLIENT_CMD_FUNC(users)
     name = cmd->argv[1];
   }
 
-  if (!conn->current_channel) {
-    cmd->client->ops->say(cmd->client, conn, SILC_CLIENT_MESSAGE_INFO, "You are not on that channel");
-    COMMAND_ERROR;
-    goto out;
-  }
-
-  /* Get the Channel ID of the channel */
-  if (!silc_idcache_find_by_name_one(conn->channel_cache, name, &id_cache)) {
-    /* XXX should resolve the channel ID; LIST command */
-    cmd->client->ops->say(cmd->client, conn, SILC_CLIENT_MESSAGE_INFO, 
-                         "You are not on that channel", name);
-    COMMAND_ERROR;
-    goto out;
-  }
-
-  channel = (SilcChannelEntry)id_cache->context;
-
-  if (!cmd->pending) {
-    /* Send USERS command to the server */
-    idp = silc_id_payload_encode(id_cache->id, SILC_ID_CHANNEL);
-    buffer = silc_command_payload_encode_va(SILC_COMMAND_USERS, 
-                                           ++conn->cmd_ident, 1, 
-                                           1, idp->data, idp->len);
-    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);
-
-    /* Register pending callback which will recall this command callback with
-       same context and reprocesses the command. When reprocessing we actually
-       display the information on the screen. */
-    silc_client_command_pending(conn, SILC_COMMAND_USERS, conn->cmd_ident, 
-                               silc_client_command_destructor,
-                               silc_client_command_users, 
-                               silc_client_command_dup(cmd));
-    cmd->pending = TRUE;
-    return;
-  }
+  /* Send USERS command to the server */
+  buffer = silc_command_payload_encode_va(SILC_COMMAND_USERS, 
+                                         ++conn->cmd_ident, 1, 
+                                         2, name, strlen(name));
+  silc_client_packet_send(cmd->client, conn->sock, SILC_PACKET_COMMAND, 
+                         NULL, 0, NULL, NULL, buffer->data, 
+                         buffer->len, TRUE);
+  silc_buffer_free(buffer);
 
   /* Notify application */
   COMMAND;
@@ -2116,6 +2095,7 @@ SILC_CLIENT_CMD_FUNC(getkey)
   SilcClientConnection conn = cmd->conn;
   SilcClient client = cmd->client;
   SilcClientEntry client_entry = NULL;
+  SilcServerEntry server_entry = NULL;
   uint32 num = 0;
   char *nickname = NULL, *server = NULL;
   SilcBuffer idp, buffer;
@@ -2127,7 +2107,8 @@ SILC_CLIENT_CMD_FUNC(getkey)
   }
 
   if (cmd->argc < 2) {
-    client->ops->say(client, conn, SILC_CLIENT_MESSAGE_INFO, "Usage: /GETKEY <nickname>");
+    client->ops->say(client, conn, SILC_CLIENT_MESSAGE_INFO, 
+                    "Usage: /GETKEY <nickname or server name>");
     COMMAND_ERROR;
     goto out;
   }
@@ -2141,20 +2122,43 @@ SILC_CLIENT_CMD_FUNC(getkey)
 
   /* Find client entry */
   client_entry = silc_idlist_get_client(client, conn, nickname, server, num,
-                                       TRUE);
+                                       FALSE);
   if (!client_entry) {
-    /* Client entry not found, it was requested thus mark this to be
-       pending command. */
-    silc_client_command_pending(conn, SILC_COMMAND_IDENTIFY, 
-                               conn->cmd_ident,  
-                               silc_client_command_destructor,
-                               silc_client_command_getkey, 
-                               silc_client_command_dup(cmd));
-    cmd->pending = 1;
-    return;
+    /* Check whether user requested server actually */
+    server_entry = silc_client_get_server(client, conn, nickname);
+
+    if (!server_entry) {
+      /* No. what ever user wants we don't have it, so resolve it. We
+        will try to resolve both client and server, one of them is
+        bound to be wrong. */
+
+      /* This will send the IDENTIFY command */
+      silc_idlist_get_client(client, conn, nickname, server, num, TRUE);
+      silc_client_command_pending(conn, SILC_COMMAND_IDENTIFY, 
+                                 conn->cmd_ident,  
+                                 silc_client_command_destructor,
+                                 silc_client_command_getkey, 
+                                 silc_client_command_dup(cmd));
+
+      /* This sends the INFO command to resolve the server. */
+      silc_client_send_command(client, conn, SILC_COMMAND_INFO,
+                              ++conn->cmd_ident, 1, 
+                              1, nickname, strlen(nickname));
+      silc_client_command_pending(conn, SILC_COMMAND_IDENTIFY, 
+                                 conn->cmd_ident,  
+                                 silc_client_command_destructor,
+                                 silc_client_command_getkey, 
+                                 silc_client_command_dup(cmd));
+
+      cmd->pending = 1;
+      return;
+    }
+
+    idp = silc_id_payload_encode(server_entry->server_id, SILC_ID_SERVER);
+  } else {
+    idp = silc_id_payload_encode(client_entry->id, SILC_ID_CLIENT);
   }
 
-  idp = silc_id_payload_encode(client_entry->id, SILC_ID_CLIENT);
   buffer = silc_command_payload_encode_va(SILC_COMMAND_GETKEY, 0, 1, 
                                          1, idp->data, idp->len);
   silc_client_packet_send(cmd->client, conn->sock, SILC_PACKET_COMMAND, NULL, 
index 10218167500bb60153b0d53a9834d0601cb307f6..79b4213028c4fdc904313209f082405ef5d28d4b 100644 (file)
        Flags for the command. These set how command behaves on different
        situations. Server sets these flags as well, but to be sure
        that our client never sends wrong commands we preserve the
-       flags on client side as well.
-
-       XXX: We preserve these so that we define them but currently we
-       don't check the flags at all.
+       flags on client side as well (actually we preserve them but
+       ignore them :)).
 
 */
 typedef struct {
index b36e5446dd958823459eea0cbb8dbae7c908dd6a..a264ec240597c0b4dc39b4a2e4b889b838e08f4d 100644 (file)
@@ -431,13 +431,20 @@ silc_client_command_reply_identify_save(SilcClientCommandReplyContext cmd,
                                        SilcCommandStatus status)
 {
   SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
-  SilcClientID *client_id;
+  SilcClient client = cmd->client;
+  SilcClientID *client_id = NULL;
+  SilcServerID *server_id = NULL;
+  SilcChannelID *channel_id = NULL;
   SilcIDCacheEntry id_cache = NULL;
-  SilcClientEntry client_entry = NULL;
+  SilcClientEntry client_entry;
+  SilcServerEntry server_entry;
+  SilcChannelEntry channel_entry;
   int argc;
   uint32 len;
   unsigned char *id_data;
-  char *nickname = NULL, *username = NULL;
+  char *name = NULL, *info = NULL;
+  SilcIDPayload idp = NULL;
+  SilcIdType id_type;
   
   argc = silc_argument_get_arg_num(cmd->args);
 
@@ -446,59 +453,119 @@ silc_client_command_reply_identify_save(SilcClientCommandReplyContext cmd,
     COMMAND_REPLY_ERROR;
     return;
   }
-  
-  client_id = silc_id_payload_parse_id(id_data, len);
-  if (!client_id) {
+  idp = silc_id_payload_parse_data(id_data, len);
+  if (!idp) {
     COMMAND_REPLY_ERROR;
     return;
   }
-  
-  nickname = silc_argument_get_arg_type(cmd->args, 3, &len);
-  username = silc_argument_get_arg_type(cmd->args, 4, &len);
 
-  /* Check if we have this client cached already. */
-  if (!silc_idcache_find_by_id_one_ext(conn->client_cache, (void *)client_id, 
-                                      NULL, NULL, 
-                                      silc_hash_client_id_compare, NULL,
-                                      &id_cache)) {
-    SILC_LOG_DEBUG(("Adding new client entry"));
+  name = silc_argument_get_arg_type(cmd->args, 3, &len);
+  info = silc_argument_get_arg_type(cmd->args, 4, &len);
 
-    client_entry = silc_calloc(1, sizeof(*client_entry));
-    client_entry->id = client_id;
-    silc_parse_nickname(nickname, &client_entry->nickname, 
-                       &client_entry->server, &client_entry->num);
-    if (username)
-      client_entry->username = strdup(username);
-    
-    /* Add client to cache */
-    silc_idcache_add(conn->client_cache, client_entry->nickname,
-                    client_id, (void *)client_entry, FALSE);
-  } else {
-    client_entry = (SilcClientEntry)id_cache->context;
-    if (client_entry->nickname)
-      silc_free(client_entry->nickname);
-    if (client_entry->server)
-      silc_free(client_entry->server);
-    if (username && client_entry->username)
-      silc_free(client_entry->username);
-    
-    SILC_LOG_DEBUG(("Updating client entry"));
+  id_type = silc_id_payload_get_type(idp);
 
-    silc_parse_nickname(nickname, &client_entry->nickname, 
-                       &client_entry->server, &client_entry->num);
-    
-    if (username)
-      client_entry->username = strdup(username);
-    
-    /* Remove the old cache entry and create a new one */
-    silc_idcache_del_by_context(conn->client_cache, client_entry);
-    silc_idcache_add(conn->client_cache, client_entry->nickname, 
-                    client_entry->id, client_entry, FALSE);
-    silc_free(client_id);
+  switch (id_type) {
+  case SILC_ID_CLIENT:
+    client_id = silc_id_payload_get_id(idp);
+
+    SILC_LOG_DEBUG(("Received client information"));
+
+    /* Check if we have this client cached already. */
+    if (!silc_idcache_find_by_id_one_ext(conn->client_cache, 
+                                        (void *)client_id, 
+                                        NULL, NULL, 
+                                        silc_hash_client_id_compare, NULL,
+                                        &id_cache)) {
+      SILC_LOG_DEBUG(("Adding new client entry"));
+      
+      client_entry = silc_calloc(1, sizeof(*client_entry));
+      client_entry->id = silc_id_dup(client_id, id_type);
+      silc_parse_nickname(name, &client_entry->nickname, 
+                         &client_entry->server, &client_entry->num);
+      if (info)
+       client_entry->username = strdup(info);
+      
+      /* Add client to cache */
+      silc_idcache_add(conn->client_cache, client_entry->nickname,
+                      client_entry->id, (void *)client_entry, FALSE);
+    } else {
+      client_entry = (SilcClientEntry)id_cache->context;
+      if (client_entry->nickname)
+       silc_free(client_entry->nickname);
+      if (client_entry->server)
+       silc_free(client_entry->server);
+      if (info && client_entry->username)
+       silc_free(client_entry->username);
+      
+      SILC_LOG_DEBUG(("Updating client entry"));
+      
+      silc_parse_nickname(name, &client_entry->nickname, 
+                         &client_entry->server, &client_entry->num);
+      
+      if (info)
+       client_entry->username = strdup(info);
+      
+      /* Remove the old cache entry and create a new one */
+      silc_idcache_del_by_context(conn->client_cache, client_entry);
+      silc_idcache_add(conn->client_cache, client_entry->nickname, 
+                      client_entry->id, client_entry, FALSE);
+    }
+
+    /* Notify application */
+    COMMAND_REPLY((ARGS, client_entry, name, info));
+    break;
+
+  case SILC_ID_SERVER:
+    server_id = silc_id_payload_get_id(idp);
+
+    SILC_LOG_DEBUG(("Received server information"));
+
+    /* Check if we have this server cached already. */
+    if (!silc_idcache_find_by_id_one(conn->server_cache, 
+                                    (void *)server_id, &id_cache)) {
+      SILC_LOG_DEBUG(("Adding new server entry"));
+      
+      server_entry = silc_calloc(1, sizeof(*server_entry));
+      server_entry->server_id = silc_id_dup(server_id, id_type);
+      server_entry->server_name = strdup(name);
+      if (info)
+       server_entry->server_info = strdup(info);
+      
+      /* Add server to cache */
+      silc_idcache_add(conn->server_cache, server_entry->server_name,
+                      server_entry->server_id, (void *)server_entry, FALSE);
+    } else {
+      server_entry = (SilcServerEntry)id_cache->context;
+    }
+
+    /* Notify application */
+    COMMAND_REPLY((ARGS, server_entry, name, info));
+    break;
+
+  case SILC_ID_CHANNEL:
+    channel_id = silc_id_payload_get_id(idp);
+
+    SILC_LOG_DEBUG(("Received channel information"));
+
+    /* Check if we have this channel cached already. */
+    if (!silc_idcache_find_by_id_one(conn->channel_cache, 
+                                    (void *)channel_id, &id_cache)) {
+      SILC_LOG_DEBUG(("Adding new channel entry"));
+      channel_entry = silc_client_new_channel_id(client, conn->sock, 
+                                                name, 0, idp);
+    } else {
+      channel_entry = (SilcChannelEntry)id_cache->context;
+    }
+
+    /* Notify application */
+    COMMAND_REPLY((ARGS, channel_entry, name, info));
+    break;
   }
 
-  /* Notify application */
-  COMMAND_REPLY((ARGS, client_entry, nickname, username));
+  silc_id_payload_free(idp);
+  silc_free(client_id);
+  silc_free(server_id);
+  silc_free(channel_id);
 }
 
 /* Received reply for IDENTIFY command. This maybe called several times
@@ -1008,6 +1075,8 @@ SILC_CLIENT_CMD_REPLY_FUNC(join)
                                       mode, idp);
   silc_id_payload_free(idp);
 
+  conn->current_channel = channel;
+
   /* Get hmac */
   hmac = silc_argument_get_arg_type(cmd->args, 11, NULL);
   if (hmac) {
@@ -1662,22 +1731,30 @@ SILC_CLIENT_CMD_REPLY_FUNC(users)
 
   /* Get channel ID */
   tmp = silc_argument_get_arg_type(cmd->args, 2, &tmp_len);
-  if (!tmp)
+  if (!tmp) {
+    COMMAND_REPLY_ERROR;
     goto out;
+  }
   channel_id = silc_id_payload_parse_id(tmp, tmp_len);
-  if (!channel_id)
+  if (!channel_id) {
+    COMMAND_REPLY_ERROR;
     goto out;
-
+  }
+  
   /* Get the list count */
   tmp = silc_argument_get_arg_type(cmd->args, 3, &tmp_len);
-  if (!tmp)
+  if (!tmp) {
+    COMMAND_REPLY_ERROR;
     goto out;
+  }
   SILC_GET32_MSB(list_count, tmp);
 
   /* Get Client ID list */
   tmp = silc_argument_get_arg_type(cmd->args, 4, &tmp_len);
-  if (!tmp)
+  if (!tmp) {
+    COMMAND_REPLY_ERROR;
     goto out;
+  }
 
   client_id_list = silc_buffer_alloc(tmp_len);
   silc_buffer_pull_tail(client_id_list, tmp_len);
@@ -1685,8 +1762,10 @@ SILC_CLIENT_CMD_REPLY_FUNC(users)
 
   /* Get client mode list */
   tmp = silc_argument_get_arg_type(cmd->args, 5, &tmp_len);
-  if (!tmp)
+  if (!tmp) {
+    COMMAND_REPLY_ERROR;
     goto out;
+  }
 
   client_mode_list = silc_buffer_alloc(tmp_len);
   silc_buffer_pull_tail(client_mode_list, tmp_len);
@@ -1695,10 +1774,18 @@ SILC_CLIENT_CMD_REPLY_FUNC(users)
   /* Get channel entry */
   if (!silc_idcache_find_by_id_one(conn->channel_cache, (void *)channel_id,
                                   &id_cache)) {
-    COMMAND_REPLY_ERROR;
-    goto out;
+    /* Resolve the channel from server */
+    silc_idlist_get_channel_by_id(cmd->client, conn, channel_id, TRUE);
+    
+    /* Register pending command callback. After we've received the channel
+       information we will reprocess this command reply by re-calling this
+       USERS command reply callback. */
+    silc_client_command_pending(conn, SILC_COMMAND_IDENTIFY, conn->cmd_ident,
+                               NULL, silc_client_command_reply_users, cmd);
+    return;
+  } else {
+    channel = (SilcChannelEntry)id_cache->context;
   }
-  channel = (SilcChannelEntry)id_cache->context;
 
   /* Remove old client list from channel. */
   silc_list_start(channel->clients);
@@ -1707,9 +1794,7 @@ SILC_CLIENT_CMD_REPLY_FUNC(users)
     silc_free(chu);
   }
 
-  /* 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! */
+  /* Cache the received Client ID's and modes. */
   for (i = 0; i < list_count; i++) {
     uint16 idp_len;
     uint32 mode;
@@ -1821,6 +1906,8 @@ SILC_CLIENT_CMD_REPLY_FUNC(getkey)
   SilcIDPayload idp = NULL;
   SilcClientID *client_id = NULL;
   SilcClientEntry client_entry;
+  SilcServerID *server_id = NULL;
+  SilcServerEntry server_entry;
   SilcSKEPKType type;
   unsigned char *tmp, *pk;
   uint32 len;
@@ -1865,8 +1952,8 @@ SILC_CLIENT_CMD_REPLY_FUNC(getkey)
 
   id_type = silc_id_payload_get_type(idp);
   if (id_type == SILC_ID_CLIENT) {
+    /* Received client's public key */
     client_id = silc_id_payload_get_id(idp);
-
     if (!silc_idcache_find_by_id_one_ext(conn->client_cache, 
                                         (void *)client_id, 
                                         NULL, NULL, 
@@ -1879,10 +1966,16 @@ SILC_CLIENT_CMD_REPLY_FUNC(getkey)
     /* Notify application */
     COMMAND_REPLY((ARGS, id_type, client_entry, public_key));
   } else if (id_type == SILC_ID_SERVER) {
-    /* XXX we don't have server entries at all */
-    goto out;
-  } else {
-    goto out;
+    /* Received server's public key */
+    server_id = silc_id_payload_get_id(idp);
+    if (!silc_idcache_find_by_id_one(conn->server_cache, (void *)server_id,
+                                    &id_cache))
+      goto out;
+
+    server_entry = (SilcServerEntry)id_cache->context;
+
+    /* Notify application */
+    COMMAND_REPLY((ARGS, id_type, server_entry, public_key));
   }
 
  out:
@@ -1892,5 +1985,6 @@ SILC_CLIENT_CMD_REPLY_FUNC(getkey)
   if (public_key)
     silc_pkcs_public_key_free(public_key);
   silc_free(client_id);
+  silc_free(server_id);
   silc_client_command_reply_free(cmd);
 }
index 6d865882b390042967e7504fb0684d4e807323b5..0599b405143659ce9df0159be1804229a117a076 100644 (file)
@@ -298,7 +298,7 @@ void silc_client_get_clients_by_list(SilcClient client,
                                    (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_argv_types[res_argc] = res_argc + 5;
       res_argc++;
     }
 
@@ -556,6 +556,134 @@ SilcChannelEntry silc_client_get_channel(SilcClient client,
   return entry;
 }
 
+/* Finds entry for channel by the channel ID. Returns the entry or NULL
+   if the entry was not found. It is found only if the client is joined
+   to the channel. */
+
+SilcChannelEntry silc_client_get_channel_by_id(SilcClient client,
+                                              SilcClientConnection conn,
+                                              SilcChannelID *channel_id)
+{
+  SilcIDCacheEntry id_cache;
+  SilcChannelEntry entry;
+
+  if (!silc_idcache_find_by_id_one(conn->channel_cache, channel_id, 
+                                  &id_cache))
+    return NULL;
+
+  entry = (SilcChannelEntry)id_cache->context;
+
+  return entry;
+}
+
+typedef struct {
+  SilcClient client;
+  SilcClientConnection conn;
+  SilcChannelID *channel_id;
+  SilcGetChannelCallback completion;
+  void *context;
+  int found;
+} *GetChannelByIDInternal;
+
+SILC_CLIENT_CMD_FUNC(get_channel_by_id_callback)
+{
+  GetChannelByIDInternal i = (GetChannelByIDInternal)context;
+  SilcChannelEntry entry;
+
+  /* Get the channel */
+  entry = silc_client_get_channel_by_id(i->client, i->conn,
+                                       i->channel_id);
+  if (entry) {
+    i->completion(i->client, i->conn, &entry, 1, i->context);
+    i->found = TRUE;
+  }
+}
+
+static void silc_client_get_channel_by_id_destructor(void *context)
+{
+  GetChannelByIDInternal i = (GetChannelByIDInternal)context;
+
+  if (i->found == FALSE)
+    i->completion(i->client, i->conn, NULL, 0, i->context);
+
+  silc_free(i->channel_id);
+  silc_free(i);
+}
+
+/* Resolves channel information from the server by the channel ID. */
+
+void silc_client_get_channel_by_id_resolve(SilcClient client,
+                                          SilcClientConnection conn,
+                                          SilcChannelID *channel_id,
+                                          SilcGetChannelCallback completion,
+                                          void *context)
+{
+  SilcBuffer idp;
+  GetChannelByIDInternal i = silc_calloc(1, sizeof(*i));
+
+  idp = silc_id_payload_encode(channel_id, SILC_ID_CHANNEL);
+  silc_client_send_command(client, conn, SILC_COMMAND_IDENTIFY, 
+                          ++conn->cmd_ident,
+                          1, 5, idp->data, idp->len);
+  silc_buffer_free(idp);
+
+  i->client = client;
+  i->conn = conn;
+  i->channel_id = silc_id_dup(channel_id, SILC_ID_CHANNEL);
+  i->completion = completion;
+  i->context = context;
+      
+  /* Add pending callback */
+  silc_client_command_pending(conn, SILC_COMMAND_IDENTIFY, 
+                             conn->cmd_ident, 
+                             silc_client_get_channel_by_id_destructor,
+                             silc_client_command_get_channel_by_id_callback, 
+                             (void *)i);
+}
+
+/* Find channel entry by ID. This routine is used internally by the library. */
+
+SilcChannelEntry silc_idlist_get_channel_by_id(SilcClient client,
+                                              SilcClientConnection conn,
+                                              SilcChannelID *channel_id,
+                                              int query)
+{
+  SilcBuffer idp;
+  SilcChannelEntry channel;
+
+  channel = silc_client_get_channel_by_id(client, conn, channel_id);
+  if (channel)
+    return channel;
+
+  if (query) {
+    idp = silc_id_payload_encode(channel_id, SILC_ID_CHANNEL);
+    silc_client_send_command(client, conn, SILC_COMMAND_IDENTIFY, 
+                            ++conn->cmd_ident,
+                            1, 5, idp->data, idp->len);
+    silc_buffer_free(idp);
+  }
+
+  return NULL;
+}
+
+/* Finds entry for server by the server name. */
+
+SilcServerEntry silc_client_get_server(SilcClient client,
+                                      SilcClientConnection conn,
+                                      char *server_name)
+{
+  SilcIDCacheEntry id_cache;
+  SilcServerEntry entry;
+
+  if (!silc_idcache_find_by_name_one(conn->server_cache, server_name, 
+                                    &id_cache))
+    return NULL;
+
+  entry = (SilcServerEntry)id_cache->context;
+
+  return entry;
+}
+
 /* Finds entry for server by the server ID. */
 
 SilcServerEntry silc_client_get_server_by_id(SilcClient client,
index 835da1aa02fbf5df3d53012c469a56de1a0e4404..32088f1545282eb00f6e9c4716717589b086e8f8 100644 (file)
@@ -88,7 +88,8 @@ typedef struct {
   SilcServerID *server_id;
 } *SilcServerEntry;
 
-/* Prototypes (some functions are defined in the silcapi.h) */
+/* Prototypes. These are used only by the library. Application should not
+   call these directly. */
 
 SilcClientEntry silc_idlist_get_client(SilcClient client,
                                       SilcClientConnection conn,
@@ -96,5 +97,9 @@ SilcClientEntry silc_idlist_get_client(SilcClient client,
                                       char *server,
                                       uint32 num,
                                       int query);
+SilcChannelEntry silc_idlist_get_channel_by_id(SilcClient client,
+                                              SilcClientConnection conn,
+                                              SilcChannelID *channel_id,
+                                              int query);
 
 #endif
index 140f2f2227aec9e84027105d24e034c0be6c3e9a..ed5c5f0f94d4a1f94577c4695e2e03f94b59f031 100644 (file)
@@ -325,6 +325,28 @@ typedef struct {
 } SilcClientOperations;
 /***/
 
+/****s* silcclient/SilcClientAPI/SilcClientParams
+ *
+ * NAME
+ *
+ *    typedef struct { ... } SilcClientParams;
+ *
+ * DESCRIPTION
+ *
+ *    Client parameters. This can be filled with proper values and
+ *    given as argument to the silc_client_alloc function. The structure
+ *    hold various parameters which affects the function of the client.
+ *
+ * SOURCE
+ */
+typedef struct {
+  /* Rekey timeout in seconds. The client will perform rekey in this
+     time interval. If set to zero, default value will be used. */
+  unsigned int rekey_secs;
+} SilcClientParams;
+/***/
+
+
 /* Initialization functions (client.c) */
 
 /****f* silcclient/SilcClientAPI/silc_client_alloc
@@ -332,6 +354,7 @@ typedef struct {
  * SYNOPSIS
  *
  *    SilcClient silc_client_alloc(SilcClientOperations *ops, 
+ *                                 SilcClientParams *params,
  *                                 void *application,
  *                                 const char *silc_version);
  *
@@ -345,7 +368,9 @@ typedef struct {
  *    version string.
  *
  ***/
-SilcClient silc_client_alloc(SilcClientOperations *ops, void *application,
+SilcClient silc_client_alloc(SilcClientOperations *ops, 
+                            SilcClientParams *params,
+                            void *application,
                             const char *silc_version);
 
 /****f* silcclient/SilcClientAPI/silc_client_free
@@ -639,7 +664,7 @@ void silc_client_send_private_message(SilcClient client,
  *
  *    Callback function given to the silc_client_get_client function. The
  *    found entries are allocated into the `clients' array. The array must
- *    not be freed by the caller, the library will free it later. If the
+ *    not be freed by the receiver, the library will free it later. If the
  *    `clients' is NULL, no such clients exist in the SILC Network.
  *
  ***/
@@ -810,6 +835,30 @@ bool silc_client_del_client_by_id(SilcClient client,
                                  SilcClientConnection conn,
                                  SilcClientID *client_id);
 
+/****f* silcclient/SilcClientAPI/SilcGetChannelCallback
+ *
+ * SYNOPSIS
+ *
+ *    typedef void (*SilcGetClientCallback)(SilcClient client,
+ *                                          SilcClientConnection conn,
+ *                                          SilcClientEntry *clients,
+ *                                          uint32 clients_count,
+ *                                          void *context);
+ *
+ * DESCRIPTION
+ *
+ *    Callback function given to the silc_client_get_channel_* functions.
+ *    The found entries are allocated into the `channels' array. The array
+ *    must not be freed by the receiver, the library will free it later.
+ *    If the `channel' is NULL, no such channel exist in the SILC Network.
+ *
+ ***/
+typedef void (*SilcGetChannelCallback)(SilcClient client,
+                                      SilcClientConnection conn,
+                                      SilcChannelEntry *channels,
+                                      uint32 channels_count,
+                                      void *context);
+
 /****f* silcclient/SilcClientAPI/silc_client_get_channel
  *
  * SYNOPSIS
@@ -829,6 +878,69 @@ SilcChannelEntry silc_client_get_channel(SilcClient client,
                                         SilcClientConnection conn,
                                         char *channel);
 
+/****f* silcclient/SilcClientAPI/silc_client_get_channel
+ *
+ * SYNOPSIS
+ *
+ *    void 
+ *    silc_client_get_channel_by_id_resolve(SilcClient client,
+ *                                          SilcClientConnection conn,
+ *                                          SilcChannelID *channel_id,
+ *                                          SilcGetClientCallback completion,
+ *                                          void *context);
+ *
+ * DESCRIPTION
+ *
+ *    Finds channel entry by the channel name. Returns the entry or NULL
+ *    if it was not found.
+ *
+ ***/
+SilcChannelEntry silc_client_get_channel_by_id(SilcClient client,
+                                              SilcClientConnection conn,
+                                              SilcChannelID *channel_id);
+
+/****f* silcclient/SilcClientAPI/silc_client_get_channel
+ *
+ * SYNOPSIS
+ *
+ *    void 
+ *    silc_client_get_channel_by_id_resolve(SilcClient client,
+ *                                          SilcClientConnection conn,
+ *                                          SilcChannelID *channel_id,
+ *                                          SilcGetClientCallback completion,
+ *                                          void *context);
+ *
+ * DESCRIPTION
+ *
+ *    Resolves the channel information (its name mainly) from the server
+ *    by the `channel_id'. Use this only if you know that you do not have
+ *    the entry cached locally.
+ *
+ ***/
+void silc_client_get_channel_by_id_resolve(SilcClient client,
+                                          SilcClientConnection conn,
+                                          SilcChannelID *channel_id,
+                                          SilcGetChannelCallback completion,
+                                          void *context);
+
+/****f* silcclient/SilcClientAPI/silc_client_get_server
+ *
+ * SYNOPSIS
+ *
+ *    SilcServerEntry silc_client_get_server(SilcClient client,
+ *                                           SilcClientConnection conn,
+ *                                           char *server_name)
+ *
+ * DESCRIPTION
+ *
+ *    Finds entry for server by the server name. Returns the entry or NULL
+ *    if the entry was not found.
+ *
+ ***/
+SilcServerEntry silc_client_get_server(SilcClient client,
+                                      SilcClientConnection conn,
+                                      char *server_name);
+
 /****f* silcclient/SilcClientAPI/silc_client_get_server_by_id
  *
  * SYNOPSIS