updates. silc.server.0.7
authorPekka Riikonen <priikone@silcnet.org>
Sun, 9 Dec 2001 17:58:37 +0000 (17:58 +0000)
committerPekka Riikonen <priikone@silcnet.org>
Sun, 9 Dec 2001 17:58:37 +0000 (17:58 +0000)
25 files changed:
CHANGES
INSTALL
Makefile.am.pre
TODO
apps/irssi/src/silc/core/client_ops.c
apps/irssi/src/silc/core/silc-servers.c
apps/silcd/command.c
includes/Makefile.am
lib/silcclient/client.c
lib/silcclient/client.h
lib/silcclient/client_channel.c
lib/silcclient/client_ftp.c
lib/silcclient/client_internal.h
lib/silcclient/client_keyagr.c
lib/silcclient/client_notify.c
lib/silcclient/client_prvmsg.c
lib/silcclient/command.c
lib/silcclient/command.h
lib/silcclient/command_reply.c
lib/silcclient/command_reply.h
lib/silcclient/idlist.c
lib/silcclient/protocol.c
lib/silcclient/silcapi.h
prepare
silc_optimize

diff --git a/CHANGES b/CHANGES
index af0fee613c80cb74dd1261808addce3bab69b4fc..e6dd27489a0527feb8af897f015f69f9a97e68d6 100644 (file)
--- a/CHANGES
+++ b/CHANGES
@@ -1,3 +1,66 @@
+Sun Dec  9 19:18:41 EET 2001  Pekka Riikonen <priikone@silcnet.org>
+
+       * Fixed the IDENTIFY command reply sending to chech better valid
+         clients.  It was possible to send incomplete list of replies.
+         Affected file silcd/command.c.
+
+Sat Dec  8 15:58:31 EET 2001  Pekka Riikonen <priikone@silcnet.org>
+
+       * Added silc_client_command[s]_[un]register functions now to
+         dynamically register the commands in client library.  Removed
+         the static table of commands.  This allows the client library
+         to call commands without causing the application to know about
+         what commands library has called.
+
+         Removed the INFO command reply kludge to detect when the command
+         was called by library.  Now library use its own command reply
+         function for INFO command.
+
+         Added function silc_client_command_call to call a command.
+         Application can use it to call command, not access the structure
+         directly.
+
+         Now all commands that are sent by the client library (not
+         explicitly sent by application) use own command reply functions.
+
+         Affected files around lib/silcclient/ and in
+         irssi/src/silc/core/.
+
+       * Fixed the WHOIS command reply sending to chech better valid
+         clients.  It was possible to send incomplete list of replies.
+
+         Fixed the WHOIS and IDENTIFY to send the request to router
+         if normal server did not do it and did not find any results.
+
+         Affected file silcd/command.c.
+
+Thu Dec  6 17:21:06 EET 2001  Pekka Riikonen <priikone@silcnet.org>
+
+       * Moved the internal data from SilcClient context into its
+         own file, not accesible to application.  Affected files
+         lib/silcclient/client.h and lib/silcclient/client_internal.h,
+         and other files in client library.
+
+Thu Dec  6 10:37:55 EET 2001  Pekka Riikonen <priikone@silcnet.org>
+
+       * Added doc/examples installation target in Makefile.am.pre.
+         A patch by salo.
+
+Tue Dec  4 17:43:19 EET 2001  Pekka Riikonen <priikone@silcnet.org>
+
+       * If NO_SUCH_CLIENT_ID notify is received for WHOIS or IDENTIFY
+         commands the found client entry will be removed from the
+         cache, after notifying application about the error.  Affected
+         file lib/silcclient/command_reply.c.
+
+       * Changed the /MSG to check for exact nickname user gave, and
+         not let `nick' match `nick@host' if it is only one found.  Now,
+         user must type the exact nickname (like nick@host2) even if
+         there are no more than one same nicks found.  This is to avoid
+         a possibility of sending nickname to wrong nickname since
+         `nick' could match `nick@host'.  Affected file is
+         irssi/src/core/silc-servers.c.
+
 Mon Dec  3 18:49:45 EET 2001  Pekka Riikonen <priikone@silcnet.org>
 
        * Do not print "you are now server operator" or similar when
diff --git a/INSTALL b/INSTALL
index 675fbe2f43dbc75ed39915216e6df7d7d02a11cd..71fb1948b6d30ce340bdbe54c44c3f4c0122fe30 100644 (file)
--- a/INSTALL
+++ b/INSTALL
@@ -19,32 +19,32 @@ give --help command to the `configure' to see all of them.  Here is listed
 few options that you might want to use.  Please refer to the rest of this
 file for more generic installation instructions.
 
---with-gmp=PATH
+`--with-gmp=PATH'
 
    If you wish to use GMP library for arbitrary precision arithmetic
 library instead of using the MPI library included in the package, you can
 give the --with-gmp=PATH option to the `configure'.  The PATH is the path
 to the GMP library in your system.
 
---disable-asm
+`--disable-asm'
 
    If you have trouble compiling the assembler optimized code in the
 package or does not want to use them, you can give the --disable-asm
 option to the `configure' script.  This will assure that assembler
 optimized code is not compiled in.
 
---enable-debug
+`--enable-debug'
 
    If you would like to enable the debugging for the compiled programs
 you can give this option to the `configure'.
 
---disable-threads
+`--disable-threads'
 
    If you do not want to compile the programs with multi threads support
 you can give --disable-threads option.  In this case all compiled programs
 will work in single thread only.
 
---enable-ipv6
+`--enable-ipv6'
 
    The `configure' will attempt to check for IPv6 support in your system.
    However, if it fails, but you still want to compile in the IPv6 support
index 5c0696c5ebbf4d3643996957589b9863d1c99148..510b418adb5806facc45c6f638bebfa086d27cb1 100644 (file)
@@ -1,14 +1,13 @@
 #
 #  Makefile.am
 #
-#  Author: Pekka Riikonen <priikone@poseidon.pspt.fi>
+#  Author: Pekka Riikonen <priikone@silcnet.org>
 #
 #  Copyright (C) 2000 - 2001 Pekka Riikonen
 #
 #  This program is free software; you can redistribute it and/or modify
 #  it under the terms of the GNU General Public License as published by
-#  the Free Software Foundation; either version 2 of the License, or
-#  (at your option) any later version.
+#  the Free Software Foundation; version 2 of the License.
 #
 #  This program is distributed in the hope that it will be useful,
 #  but WITHOUT ANY WARRANTY; without even the implied warranty of
@@ -70,6 +69,12 @@ doc-install:
        $(INSTALL_DATA) $(srcdir)/INSTALL $(docdir)/
        $(INSTALL_DATA) $(srcdir)/TODO $(docdir)/
 
+examples-install:
+       -mkdir -p $(docdir)/examples/
+       $(INSTALL_DATA) $(srcdir)/doc/examples/README $(docdir)/examples/
+       $(INSTALL_DATA) $(srcdir)/doc/examples/silc* $(docdir)/examples/
+       $(INSTALL_DATA) $(srcdir)/doc/examples/cell* $(docdir)/examples/
+
 etc-install:
        -@if test '!' -f $(etcdir)/silcd.conf ; then \
          $(INSTALL_DATA) $(srcdir)/doc/example_silcd.conf \
@@ -84,5 +89,5 @@ etc-install:
 if SILC_DIST_CLIENT
 install-data-hook: install-dirs sim-install doc-install etc-install
 else
-install-data-hook: install-dirs generate-server-key sim-install doc-install etc-install
+install-data-hook: install-dirs generate-server-key sim-install doc-install examples-install etc-install
 endif
diff --git a/TODO b/TODO
index 966e812bcfa735511a2209c5fcbc685405834e6a..361ea7a895b88955b770e48dd91cc5e706ce3165 100644 (file)
--- a/TODO
+++ b/TODO
@@ -30,16 +30,7 @@ TODO/bugs in Irssi SILC client
 TODO/bugs In SILC Client Library
 ================================
 
- o Process the NO_SUCH_CLIENT_ID for WHOIS and IDENTIFY, since it can
-   be received for example after sending MSG to non-existent client.
-   It actually should be done always when it is received and the old
-   entry should be removed.
-
-   Doing this now however causes that /msg nick might give "no such nick"
-   for the first time, and then after the entry is removed /msg nick
-   may actually to go any nick client, which is not desired behaviour.
-   The /msg must be fixed to use the specific nickname user typed 
-   (nick must not match nick@host).
+ o N/A
 
 
 TODO/bugs In SILC Server
@@ -84,7 +75,38 @@ TODO/bugs In SILC Libraries
    than on Unix.  Do it with threads on WIN32.  The function works but
    is not actually async currently.
 
- o Do not let the silcdefs.h lay around in distributions.
+
+TODO in SILC Protocol
+=====================
+
+ o Add "request parameters" or similar to the WHOIS command, which can
+   be used to request various parameters (something not returned by
+   standard WHOIS command) about clients (info that could be fetched
+   even from clients).  Additional specification (or appendix) should 
+   be done to define the payload and the parameters.  It could be used
+   to make the WHOIS command support various search conditions as well.
+   This would be the way to extend the WHOIS command to support various
+   new features without always making the command incompatible to previous
+   version.  To be included in protocol version 1.1.
+
+ o Re-define the Status Payload: it is now 16 bits, split it into two
+   8 bits fields.  First field includes status types from 0 - 9 and
+   10 - n *if* it is not an list of errors.  If it is list of errors then
+   the first field includes 1, 2 and/or 3, and the second field includes
+   the error status 10 - n.  This way it is possible to send multiple
+   errors (list of errors) and we have a way to tell the receiver that
+   there will be other errors as well.  The second field is used only
+   if there is list of errors.  If normal status, or normal (single)
+   error status the second field is set to zero, and must be ignored.
+   Hence, the status works same way as now except for list of errors.
+   To be included in protocol version 1.1.
+
+ o Define that WHOIS and IDENTIFY commands must send list of errors
+   if multiple Client ID (or Channel ID and Server ID for IDENTIFY) was
+   requested and was not found.  Each unfound entry must cause an error
+   command reply to the sender.  Also define that errors must be sent
+   *after* sending successfully found entries (this way receiver may
+   ignore them).  To be included in protocol version 1.1.
 
 
 TODO After 1.0
index 76be0744d7adff60ffb07546cc97a1a56d37010b..f6dac8aec0db84efb94381362710eabde81fb464 100644 (file)
@@ -433,6 +433,7 @@ silc_command_reply(SilcClient client, SilcClientConnection conn,
            silc_free(client_id);
          }
        }
+       break;
       }
       
       if (!success)
@@ -521,10 +522,6 @@ silc_command_reply(SilcClient client, SilcClientConnection conn,
     {
       SilcClientEntry client_entry;
       
-      /* Identify command is used only internally by the client library
-        but it still might send some interesting stuff for user interface
-        so let's print errors. */
-      
       if (status == SILC_STATUS_ERR_NO_SUCH_NICK) {
        /* Print the unknown nick for user */
        unsigned char *tmp =
@@ -552,9 +549,11 @@ silc_command_reply(SilcClient client, SilcClientConnection conn,
            silc_free(client_id);
          }
        }
+       break;
       }
+
+      break;
     }
-    break;
 
   case SILC_COMMAND_WHOWAS:
     {
index 8e476fa19f5da47cb1c1ff4469869fbac1957b8d..9fdfd4a55bf22a24351000b2ec2aa7b69c33a9f7 100644 (file)
@@ -80,7 +80,8 @@ static void silc_send_msg_clients(SilcClient client,
   char *nickname = NULL;
 
   if (!clients_count) {
-    printtext(NULL, NULL, MSGLEVEL_CLIENTERROR, "Unknown nick: %s", rec->nick);
+    printtext(NULL, NULL, MSGLEVEL_CLIENTERROR, 
+             "%s: There is no such client", rec->nick);
   } else {
     if (clients_count > 1) {
       silc_parse_userfqdn(rec->nick, &nickname, NULL);
@@ -91,8 +92,8 @@ static void silc_send_msg_clients(SilcClient client,
                                              nickname, rec->nick, 
                                              &clients_count);
       if (!clients) {
-       printtext(NULL, NULL, MSGLEVEL_CLIENTERROR, "Unknown nick: %s", 
-                 rec->nick);
+       printtext(NULL, NULL, MSGLEVEL_CLIENTERROR, 
+                 "%s: There is no such client", rec->nick);
        silc_free(nickname);
        goto out;
       }
@@ -101,6 +102,16 @@ static void silc_send_msg_clients(SilcClient client,
 
     target = clients[0];
 
+    /* Still check for exact math for nickname, this compares the
+       real (formatted) nickname and the nick (maybe formatted) that
+       use gave. This is to assure that `nick' does not match 
+       `nick@host'. */
+    if (strcasecmp(rec->nick, clients[0]->nickname)) {
+      printtext(NULL, NULL, MSGLEVEL_CLIENTERROR, 
+               "%s: There is no such client", rec->nick);
+      goto out;
+    }
+
     /* Send the private message */
     silc_client_send_private_message(client, conn, target, 0,
                                     rec->msg, strlen(rec->msg),
@@ -321,14 +332,14 @@ void silc_command_exec(SILC_SERVER_REC *server,
   unsigned char **argv;
   uint32 *argv_lens, *argv_types;
   char *data, *tmpcmd;
-  SilcClientCommand *cmd;
+  SilcClientCommand cmd;
   SilcClientCommandContext ctx;
 
   g_return_if_fail(server != NULL);
 
   tmpcmd = g_strdup(command); 
   g_strup(tmpcmd);
-  cmd = silc_client_command_find(tmpcmd);
+  cmd = silc_client_command_find(silc_client, tmpcmd);
   g_free(tmpcmd);
   if (cmd == NULL)
     return;
@@ -351,7 +362,7 @@ void silc_command_exec(SILC_SERVER_REC *server,
   ctx->argv_types = argv_types;
   
   /* Execute command */
-  (*cmd->cb)(ctx, NULL);
+  silc_client_command_call(cmd, ctx);
 }
 
 /* Generic command function to call any SILC command directly. */
index a1e5e16c7d953022651e80ab7c22033429f60115..e9ee59679446ce6942d3a17a1944803a7c91246d 100644 (file)
@@ -35,7 +35,7 @@ silc_server_command_send_status_data(SilcServerCommandContext cmd,
                                     SilcCommand command,
                                     SilcCommandStatus status,
                                     uint32 arg_type,
-                                    unsigned char *arg,
+                                    const unsigned char *arg,
                                     uint32 arg_len);
 static bool
 silc_server_command_pending_error_check(SilcServerCommandContext cmd,
@@ -402,7 +402,7 @@ silc_server_command_send_status_data(SilcServerCommandContext cmd,
                                     SilcCommand command,
                                     SilcCommandStatus status,
                                     uint32 arg_type,
-                                    unsigned char *arg,
+                                    const unsigned char *arg,
                                     uint32 arg_len)
 {
   SilcBuffer buffer;
@@ -684,11 +684,13 @@ static void
 silc_server_command_whois_send_reply(SilcServerCommandContext cmd,
                                     SilcClientEntry *clients,
                                     uint32 clients_count,
-                                    int count)
+                                    int count,
+                                    const char *nickname,
+                                    SilcClientID **client_ids)
 {
   SilcServer server = cmd->server;
   char *tmp;
-  int i, k, len;
+  int i, k, len, valid_count;
   SilcBuffer packet, idp, channels;
   SilcClientEntry entry;
   SilcCommandStatus status;
@@ -698,71 +700,60 @@ silc_server_command_whois_send_reply(SilcServerCommandContext cmd,
   unsigned char *fingerprint;
   SilcSocketConnection hsock;
 
-  len = 0;
-  for (i = 0; i < clients_count; i++)
+  /* Process only valid clients and ignore those that are not registered. */
+  valid_count = 0;
+  for (i = 0; i < clients_count; i++) {
     if (clients[i]->data.status & SILC_IDLIST_STATUS_REGISTERED)
-      len++;
+      valid_count++;
+    else
+      clients[i] = NULL;
+  }
 
-  if (len == 0 && clients_count) {
-    entry = clients[0];
-    if (entry->nickname) {
+  if (!valid_count) {
+    /* No valid clients found, send error reply */
+    if (nickname) {
       silc_server_command_send_status_data(cmd, SILC_COMMAND_WHOIS,
                                           SILC_STATUS_ERR_NO_SUCH_NICK,
-                                          3, entry->nickname, 
-                                          strlen(entry->nickname));
-    } else {
-      SilcBuffer idp = silc_id_payload_encode(entry->id, SILC_ID_CLIENT);
+                                          3, nickname, strlen(nickname));
+    } else if (client_ids && client_ids[0]) {
+      SilcBuffer idp = silc_id_payload_encode(client_ids[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);
     }
-
     return;
   }
 
-  status = SILC_STATUS_OK;
-  if (len > 1)
+  /* Start processing found clients. */
+  if (valid_count > 1)
     status = SILC_STATUS_LIST_START;
+  else
+    status = SILC_STATUS_OK;
 
   for (i = 0, k = 0; i < clients_count; i++) {
     entry = clients[i];
+    if (!entry)
+      continue;
 
-    if (!(entry->data.status & SILC_IDLIST_STATUS_REGISTERED)) {
-      if (clients_count == 1) {
-       if (entry->nickname) {
-         silc_server_command_send_status_data(cmd, SILC_COMMAND_WHOIS,
-                                              SILC_STATUS_ERR_NO_SUCH_NICK,
-                                              3, entry->nickname, 
-                                              strlen(entry->nickname));
-       } else {
-         SilcBuffer idp = silc_id_payload_encode(entry->id, SILC_ID_CLIENT);
-         silc_server_command_send_status_data(cmd, SILC_COMMAND_WHOIS,
-                                    SILC_STATUS_ERR_NO_SUCH_CLIENT_ID,
-                                              2, idp->data, idp->len);
-         silc_buffer_free(idp);
-       }
-      }
+#if 1
+    /* XXX REMOVE */
+    /* Sanity check, however these should never fail. However, as
+       this sanity check has been added here they have failed. */
+    if (!entry->nickname || !entry->username || !entry->userinfo) {
+      SILC_LOG_ERROR(("********* if (!entry->nickname || !entry->username "
+                     "|| !entry->userinfo) triggered: should have not!"));
       continue;
     }
+#endif
 
     if (k >= 1)
       status = SILC_STATUS_LIST_ITEM;
-
-    if (clients_count > 1 && k == clients_count - 1)
+    if (valid_count > 1 && k == valid_count - 1)
       status = SILC_STATUS_LIST_END;
-
     if (count && k - 1 == count)
       status = SILC_STATUS_LIST_END;
 
-    if (count && k - 1 > count)
-      break;
-
-    /* Sanity check, however these should never fail. However, as
-       this sanity check has been added here they have failed. */
-    if (!entry->nickname || !entry->username || !entry->userinfo)
-      continue;
-      
     /* Send WHOIS reply */
     idp = silc_id_payload_encode(entry->id, SILC_ID_CLIENT);
     tmp = silc_argument_get_first_arg(cmd->args, NULL);
@@ -832,6 +823,34 @@ silc_server_command_whois_send_reply(SilcServerCommandContext cmd,
   }
 }
 
+static void 
+silc_server_command_whois_send_router(SilcServerCommandContext cmd)
+{
+  SilcServer server = cmd->server;
+  SilcBuffer tmpbuf;
+  uint16 old_ident;
+
+  old_ident = silc_command_get_ident(cmd->payload);
+  silc_command_set_ident(cmd->payload, ++server->cmd_ident);
+  tmpbuf = silc_command_payload_encode_payload(cmd->payload);
+
+  /* Send WHOIS command to our router */
+  silc_server_packet_send(server, (SilcSocketConnection)
+                         server->router->connection,
+                         SILC_PACKET_COMMAND, cmd->packet->flags,
+                         tmpbuf->data, tmpbuf->len, TRUE);
+
+  /* Reprocess this packet after received reply from router */
+  silc_server_command_pending(server, SILC_COMMAND_WHOIS, 
+                             silc_command_get_ident(cmd->payload),
+                             silc_server_command_destructor,
+                             silc_server_command_whois,
+                             silc_server_command_dup(cmd));
+  cmd->pending = TRUE;
+  silc_command_set_ident(cmd->payload, old_ident);
+  silc_buffer_free(tmpbuf);
+}
+
 static int
 silc_server_command_whois_process(SilcServerCommandContext cmd)
 {
@@ -854,34 +873,10 @@ silc_server_command_whois_process(SilcServerCommandContext cmd)
      Since nicknames can be expanded into many clients we need to send it
      to router.  If the WHOIS included only client ID's we will check them
      first locally since we just might have them. */
-  if (nick && !client_id_count &&
-      cmd->sock->type == SILC_SOCKET_TYPE_CLIENT &&
-      server->server_type == SILC_SERVER && !cmd->pending && 
+  if (nick && !client_id_count && cmd->sock->type == SILC_SOCKET_TYPE_CLIENT &&
+      server->server_type == SILC_SERVER && !cmd->pending &&
       !server->standalone) {
-    SilcBuffer tmpbuf;
-    uint16 old_ident;
-
-    old_ident = silc_command_get_ident(cmd->payload);
-    silc_command_set_ident(cmd->payload, ++server->cmd_ident);
-    tmpbuf = silc_command_payload_encode_payload(cmd->payload);
-
-    /* Send WHOIS command to our router */
-    silc_server_packet_send(server, (SilcSocketConnection)
-                           server->router->connection,
-                           SILC_PACKET_COMMAND, cmd->packet->flags,
-                           tmpbuf->data, tmpbuf->len, TRUE);
-
-    /* Reprocess this packet after received reply from router */
-    silc_server_command_pending(server, SILC_COMMAND_WHOIS, 
-                               silc_command_get_ident(cmd->payload),
-                               silc_server_command_destructor,
-                               silc_server_command_whois,
-                               silc_server_command_dup(cmd));
-    cmd->pending = TRUE;
-
-    silc_command_set_ident(cmd->payload, old_ident);
-
-    silc_buffer_free(tmpbuf);
+    silc_server_command_whois_send_router(cmd);
     ret = -1;
     goto out;
   }
@@ -904,9 +899,20 @@ silc_server_command_whois_process(SilcServerCommandContext cmd)
        clients = silc_realloc(clients, sizeof(*clients) * 
                               (clients_count + 1));
        clients[clients_count++] = entry;
+      } else {
+       /* If we are normal server and did not send the request first to router
+          do it now, since we do not have the Client ID information. */
+       if (cmd->sock->type == SILC_SOCKET_TYPE_CLIENT &&
+           server->server_type == SILC_SERVER && !cmd->pending && 
+           !server->standalone) {
+         silc_server_command_whois_send_router(cmd);
+         ret = -1;
+         goto out;
+       }
       }
     }
   } else {
+    /* Find by nickname */
     if (!silc_idlist_get_clients_by_hash(server->local_list, 
                                         nick, server->md5hash,
                                         &clients, &clients_count))
@@ -924,6 +930,16 @@ silc_server_command_whois_process(SilcServerCommandContext cmd)
   }
   
   if (!clients) {
+    /* If we are normal server and did not send the request first to router
+       do it now, since we do not have the information. */
+    if (cmd->sock->type == SILC_SOCKET_TYPE_CLIENT &&
+       server->server_type == SILC_SERVER && !cmd->pending && 
+       !server->standalone) {
+      silc_server_command_whois_send_router(cmd);
+      ret = -1;
+      goto out;
+    }
+
     /* 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,
@@ -951,7 +967,7 @@ silc_server_command_whois_process(SilcServerCommandContext cmd)
 
   /* Send the command reply */
   silc_server_command_whois_send_reply(cmd, clients, clients_count,
-                                      count);
+                                      count, nick, client_id);
 
  out:
   if (client_id_count) {
@@ -1088,9 +1104,7 @@ silc_server_command_whowas_send_reply(SilcServerCommandContext cmd,
     /* We will take only clients that are not valid anymore. They are the
        ones that are not registered anymore but still have a ID. They
        have disconnected us, and thus valid for WHOWAS. */
-    if (entry->data.status & SILC_IDLIST_STATUS_REGISTERED)
-      continue;
-    if (entry->id == NULL)
+    if (entry->data.status & SILC_IDLIST_STATUS_REGISTERED || !entry->id)
       continue;
 
     if (count && i - 1 == count)
@@ -1100,19 +1114,31 @@ silc_server_command_whowas_send_reply(SilcServerCommandContext cmd,
 
     if (clients_count > 2)
       status = SILC_STATUS_LIST_ITEM;
-
     if (clients_count > 1 && i == clients_count - 1)
       status = SILC_STATUS_LIST_END;
 
     /* Sanity check, however these should never fail. However, as
        this sanity check has been added here they have failed. */
-    if (!entry->nickname || !entry->username)
+    if (!entry->nickname || !entry->username || !entry->userinfo) {
+      SILC_LOG_ERROR(("********* if (!entry->nickname || !entry->username "
+                     "|| !entry->userinfo) triggered: should have not!"));
       continue;
+    }
+
+#if 1
+    /* XXX REMOVE */
+    /* Sanity check, however these should never fail. However, as
+       this sanity check has been added here they have failed. */
+    if (!entry->nickname || !entry->username) {
+      SILC_LOG_ERROR(("********* if (!entry->nickname || !entry->username) "
+                     "triggered: should have not!"));
+      continue;
+    }
+#endif
       
     /* Send WHOWAS 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));
 
@@ -1279,7 +1305,35 @@ SILC_SERVER_CMD_FUNC(whowas)
 
 ******************************************************************************/
 
-static bool
+static void 
+silc_server_command_identify_send_router(SilcServerCommandContext cmd)
+{
+  SilcServer server = cmd->server;
+  SilcBuffer tmpbuf;
+  uint16 old_ident;
+
+  old_ident = silc_command_get_ident(cmd->payload);
+  silc_command_set_ident(cmd->payload, ++server->cmd_ident);
+  tmpbuf = silc_command_payload_encode_payload(cmd->payload);
+
+  /* Send IDENTIFY command to our router */
+  silc_server_packet_send(server, (SilcSocketConnection)
+                         server->router->connection,
+                         SILC_PACKET_COMMAND, cmd->packet->flags,
+                         tmpbuf->data, tmpbuf->len, TRUE);
+
+  /* Reprocess this packet after received reply from router */
+  silc_server_command_pending(server, SILC_COMMAND_IDENTIFY, 
+                             silc_command_get_ident(cmd->payload),
+                             silc_server_command_destructor,
+                             silc_server_command_identify,
+                             silc_server_command_dup(cmd));
+  cmd->pending = TRUE;
+  silc_command_set_ident(cmd->payload, old_ident);
+  silc_buffer_free(tmpbuf);
+}
+
+static int
 silc_server_command_identify_parse(SilcServerCommandContext cmd,
                                   SilcClientEntry **clients,
                                   uint32 *clients_count,
@@ -1287,8 +1341,7 @@ silc_server_command_identify_parse(SilcServerCommandContext cmd,
                                   uint32 *servers_count,
                                   SilcChannelEntry **channels,
                                   uint32 *channels_count,
-                                  uint32 *count,
-                                  bool *names)
+                                  uint32 *count)
 {
   SilcServer server = cmd->server;
   unsigned char *tmp;
@@ -1309,7 +1362,15 @@ silc_server_command_identify_parse(SilcServerCommandContext cmd,
   tmp = silc_argument_get_arg_type(cmd->args, 5, &len);
   if (!tmp) {
     /* No ID, get the names. */
-    *names = TRUE;
+
+    /* If we are normal server and have not resolved information from
+       router yet, do so now. */
+    if (cmd->sock->type == SILC_SOCKET_TYPE_CLIENT && 
+       server->server_type == SILC_SERVER && !cmd->pending && 
+       !server->standalone) {
+      silc_server_command_identify_send_router(cmd);
+      return -1;
+    }
 
     /* Try to get nickname@server. */
     tmp = silc_argument_get_arg_type(cmd->args, 1, NULL);
@@ -1338,10 +1399,11 @@ silc_server_command_identify_parse(SilcServerCommandContext cmd,
       silc_free(nick_server);
 
       if (!(*clients)) {
+       /* the nickname does not exist, send error reply */
        silc_server_command_send_status_data(cmd, SILC_COMMAND_IDENTIFY,
                                             SILC_STATUS_ERR_NO_SUCH_NICK,
                                             3, tmp, strlen(tmp));
-       return FALSE;
+       return 0;
       }
     }
 
@@ -1360,10 +1422,11 @@ silc_server_command_identify_parse(SilcServerCommandContext cmd,
       }
 
       if (!(*servers)) {
+       /* the server does not exist, send error reply */
        silc_server_command_send_status_data(cmd, SILC_COMMAND_IDENTIFY,
                                             SILC_STATUS_ERR_NO_SUCH_SERVER,
                                             3, tmp, strlen(tmp));
-       return FALSE;
+       return 0;
       }
     }
 
@@ -1382,22 +1445,22 @@ silc_server_command_identify_parse(SilcServerCommandContext cmd,
       }
 
       if (!(*channels)) {
+       /* The channel does not exist, send error reply */
        silc_server_command_send_status_data(cmd, SILC_COMMAND_IDENTIFY,
                                             SILC_STATUS_ERR_NO_SUCH_CHANNEL,
                                             3, tmp, strlen(tmp));
-       return FALSE;
+       return 0;
       }
     }
 
     if (!(*clients) && !(*servers) && !(*channels)) {
       silc_server_command_send_status_reply(cmd, SILC_COMMAND_IDENTIFY,
                                            SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
-      return FALSE;
+      return 0;
     }
   } else {
     /* Command includes ID, we must use that.  Also check whether the command
        has more than one ID set - take them all. */
-    *names = FALSE;
 
     /* Take all ID's from the command packet */
     for (i = 0; i < argc; i++) {
@@ -1412,9 +1475,10 @@ silc_server_command_identify_parse(SilcServerCommandContext cmd,
        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;
+       silc_server_command_send_status_reply(
+                                      cmd, SILC_COMMAND_IDENTIFY,
+                                      SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
+       return 0;
       }
 
       id = silc_id_payload_get_id(idp);
@@ -1432,10 +1496,23 @@ silc_server_command_identify_parse(SilcServerCommandContext cmd,
                                  (*clients_count + 1));
          (*clients)[(*clients_count)++] = (SilcClientEntry)entry;
        } else {
-         silc_server_command_send_status_data(cmd, SILC_COMMAND_IDENTIFY,
+         /* If we are normal server and have not resolved information from
+            router yet, do so now. */
+         if (cmd->sock->type == SILC_SOCKET_TYPE_CLIENT && 
+             server->server_type == SILC_SERVER && !cmd->pending && 
+             !server->standalone) {
+           silc_server_command_identify_send_router(cmd);
+           silc_free(*clients);
+           silc_free(*servers);
+           silc_free(*channels);
+           return -1;
+         } else {
+           silc_server_command_send_status_data(
+                                       cmd, SILC_COMMAND_IDENTIFY,
                                        SILC_STATUS_ERR_NO_SUCH_CLIENT_ID,
-                                              2, tmp, len);
-         error = TRUE;
+                                       2, tmp, len);
+           error = TRUE;
+         }
        }
 
        break;
@@ -1451,10 +1528,23 @@ silc_server_command_identify_parse(SilcServerCommandContext cmd,
                                  (*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;
+         /* If we are normal server and have not resolved information from
+            router yet, do so now. */
+         if (cmd->sock->type == SILC_SOCKET_TYPE_CLIENT && 
+             server->server_type == SILC_SERVER && !cmd->pending && 
+             !server->standalone) {
+           silc_server_command_identify_send_router(cmd);
+           silc_free(*clients);
+           silc_free(*servers);
+           silc_free(*channels);
+           return -1;
+         } else {
+           silc_server_command_send_status_data(
+                                        cmd, SILC_COMMAND_IDENTIFY,
+                                        SILC_STATUS_ERR_NO_SUCH_SERVER_ID,
+                                        2, tmp, len);
+           error = TRUE;
+         }
        }
        break;
        
@@ -1469,10 +1559,23 @@ silc_server_command_identify_parse(SilcServerCommandContext cmd,
                                   (*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;
+         /* If we are normal server and have not resolved information from
+            router yet, do so now. */
+         if (cmd->sock->type == SILC_SOCKET_TYPE_CLIENT && 
+             server->server_type == SILC_SERVER && !cmd->pending && 
+             !server->standalone) {
+           silc_server_command_identify_send_router(cmd);
+           silc_free(*clients);
+           silc_free(*servers);
+           silc_free(*channels);
+           return -1;
+         } else {
+           silc_server_command_send_status_data(
+                                        cmd, SILC_COMMAND_IDENTIFY,
+                                        SILC_STATUS_ERR_NO_SUCH_CHANNEL_ID,
+                                        2, tmp, len);
+           error = TRUE;
+         }
        }
        break;
       }
@@ -1495,7 +1598,7 @@ silc_server_command_identify_parse(SilcServerCommandContext cmd,
   else
     *count = 0;
 
-  return TRUE;
+  return 1;
 }
 
 /* Checks that all mandatory fields in client entry are present. If not
@@ -1652,7 +1755,7 @@ silc_server_command_identify_send_reply(SilcServerCommandContext cmd,
                                        int count)
 {
   SilcServer server = cmd->server;
-  int i, k, len;
+  int i, k, len, valid_count;
   SilcBuffer packet, idp;
   SilcCommandStatus status;
   uint16 ident = silc_command_get_ident(cmd->payload);
@@ -1664,62 +1767,58 @@ silc_server_command_identify_send_reply(SilcServerCommandContext cmd,
   if (clients) {
     SilcClientEntry entry;
 
-    len = 0;
-    for (i = 0; i < clients_count; i++)
+    /* Process only valid entries. */
+    valid_count = 0;
+    for (i = 0; i < clients_count; i++) {
       if (clients[i]->data.status & SILC_IDLIST_STATUS_REGISTERED)
-       len++;
+       valid_count++;
+      else
+       clients[i] = NULL;
+    }
+
+    if (!valid_count) {
+      /* No valid entries found at all, just send error */
+      unsigned char *tmp;
 
-    if (len == 0 && clients_count) {
-      entry = clients[0];
-      if (entry->nickname) {
+      tmp = silc_argument_get_arg_type(cmd->args, 1, NULL);
+      if (tmp) {
        silc_server_command_send_status_data(cmd, SILC_COMMAND_IDENTIFY,
                                             SILC_STATUS_ERR_NO_SUCH_NICK,
-                                            3, entry->nickname, 
-                                            strlen(entry->nickname));
+                                            3, tmp, strlen(tmp));
       } else {
-       SilcBuffer idp = silc_id_payload_encode(entry->id, SILC_ID_CLIENT);
+       tmp = silc_argument_get_arg_type(cmd->args, 5, (uint32 *)&len);
        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);
+                                            2, tmp, len);
       }
-      
       return;
     }
 
+    /* Process all valid client entries and send command replies */
+
     if (len > 1)
       status = SILC_STATUS_LIST_START;
 
     for (i = 0, k = 0; i < clients_count; i++) {
       entry = clients[i];
-      
-      if (!(entry->data.status & SILC_IDLIST_STATUS_REGISTERED)) {
-       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);
-       }
+      if (!entry)
        continue;
-      }
-      
+
       if (k >= 1)
        status = SILC_STATUS_LIST_ITEM;
-      if (clients_count > 1 && k == clients_count - 1 
+      if (valid_count > 1 && k == valid_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);
@@ -1732,7 +1831,7 @@ silc_server_command_identify_send_reply(SilcServerCommandContext cmd,
                  server->server_name, len);
        }
       }
-      
+
       if (!entry->username) {
        packet = silc_command_reply_payload_encode_va(SILC_COMMAND_IDENTIFY,
                                                      status, ident, 2,
@@ -1764,9 +1863,6 @@ silc_server_command_identify_send_reply(SilcServerCommandContext cmd,
     }
   }
 
-  status = (status == SILC_STATUS_LIST_ITEM ? 
-           SILC_STATUS_LIST_ITEM : SILC_STATUS_OK);
-
   if (servers) {
     SilcServerEntry entry;
 
@@ -1804,9 +1900,6 @@ silc_server_command_identify_send_reply(SilcServerCommandContext cmd,
     }
   }
 
-  status = (status == SILC_STATUS_LIST_ITEM ? 
-           SILC_STATUS_LIST_ITEM : SILC_STATUS_OK);
-
   if (channels) {
     SilcChannelEntry entry;
 
@@ -1848,57 +1941,21 @@ silc_server_command_identify_send_reply(SilcServerCommandContext cmd,
 static int
 silc_server_command_identify_process(SilcServerCommandContext cmd)
 {
-  SilcServer server = cmd->server;
   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;
-  bool names;
 
   /* Parse the IDENTIFY request */
-  if (!silc_server_command_identify_parse(cmd,
-                                         &clients, &clients_count,
-                                         &servers, &servers_count,
-                                         &channels, &channels_count,
-                                         &count, &names))
-    return 0;
-
-  /* Send the IDENTIFY request to the router only if it included nickname.
-     Since nicknames can be expanded into many clients we need to send it
-     to router.  If the IDENTIFY included only client ID's we will check them
-     first locally since we just might have them. */
-  if (names && cmd->sock->type == SILC_SOCKET_TYPE_CLIENT && 
-      server->server_type == SILC_SERVER && !cmd->pending && 
-      !server->standalone) {
-    SilcBuffer tmpbuf;
-    uint16 old_ident;
-
-    old_ident = silc_command_get_ident(cmd->payload);
-    silc_command_set_ident(cmd->payload, ++server->cmd_ident);
-    tmpbuf = silc_command_payload_encode_payload(cmd->payload);
-
-    /* Send IDENTIFY command to our router */
-    silc_server_packet_send(server, (SilcSocketConnection)
-                           server->router->connection,
-                           SILC_PACKET_COMMAND, cmd->packet->flags,
-                           tmpbuf->data, tmpbuf->len, TRUE);
-
-    /* Reprocess this packet after received reply from router */
-    silc_server_command_pending(server, SILC_COMMAND_IDENTIFY, 
-                               silc_command_get_ident(cmd->payload),
-                               silc_server_command_destructor,
-                               silc_server_command_identify,
-                               silc_server_command_dup(cmd));
-    cmd->pending = TRUE;
-
-    silc_command_set_ident(cmd->payload, old_ident);
-
-    silc_buffer_free(tmpbuf);
-    ret = -1;
-    goto out;
-  }
+  ret = silc_server_command_identify_parse(cmd,
+                                          &clients, &clients_count,
+                                          &servers, &servers_count,
+                                          &channels, &channels_count,
+                                          &count);
+  if (ret < 1)
+    return ret;
 
   /* Check that all mandatory fields are present and request those data
      from the server who owns the client if necessary. */
@@ -1919,7 +1976,6 @@ silc_server_command_identify_process(SilcServerCommandContext cmd)
   silc_free(clients);
   silc_free(servers);
   silc_free(channels);
-
   return ret;
 }
 
index cd5ecd16d3a5d7f8785f5944c6e60ef9e6f63040..256dc3b8dc495fb939f24bf4e053b1d0b2a25566 100644 (file)
@@ -32,4 +32,10 @@ include_HEADERS = \
 endif
 
 EXTRA_DIST = \
-       silcdefs.h.in *.h
+       bitmove.h \
+       clientlibincludes.h \
+       silcincludes.h \
+       silcwin32.h \
+       version.h \
+       version_internal.h \
+       silcdefs.h.in
index 4676dc514305f4d82f77f471497965fa4d8c95e6..fe6705968ec2764b9120cbc992ddb9f430aacf6b 100644 (file)
@@ -53,24 +53,28 @@ SilcClient silc_client_alloc(SilcClientOperations *ops,
 
   new_client = silc_calloc(1, sizeof(*new_client));
   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));
+
+  new_client->internal = silc_calloc(1, sizeof(*new_client->internal));
+  new_client->internal->ops = ops;
+  new_client->internal->params = 
+    silc_calloc(1, sizeof(*new_client->internal->params));
+  new_client->internal->silc_client_version = strdup(silc_version);
 
   if (params)
-    memcpy(new_client->params, params, sizeof(*params));
+    memcpy(new_client->internal->params, params, sizeof(*params));
 
-  if (!new_client->params->task_max)
-    new_client->params->task_max = 200;
+  if (!new_client->internal->params->task_max)
+    new_client->internal->params->task_max = 200;
 
-  if (!new_client->params->rekey_secs)
-    new_client->params->rekey_secs = 3600;
+  if (!new_client->internal->params->rekey_secs)
+    new_client->internal->params->rekey_secs = 3600;
 
-  if (!new_client->params->connauth_request_secs)
-    new_client->params->connauth_request_secs = 2;
+  if (!new_client->internal->params->connauth_request_secs)
+    new_client->internal->params->connauth_request_secs = 2;
 
-  new_client->params->
-    nickname_format[sizeof(new_client->params->nickname_format) - 1] = 0;
+  new_client->internal->params->
+    nickname_format[sizeof(new_client->internal->
+                          params->nickname_format) - 1] = 0;
 
   return new_client;
 }
@@ -83,8 +87,9 @@ 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->internal->params);
+    silc_free(client->internal->silc_client_version);
+    silc_free(client->internal);
     silc_free(client);
   }
 }
@@ -98,11 +103,11 @@ int silc_client_init(SilcClient client)
   SILC_LOG_DEBUG(("Initializing client"));
 
   /* Initialize hash functions for client to use */
-  silc_hash_alloc("md5", &client->md5hash);
-  silc_hash_alloc("sha1", &client->sha1hash);
+  silc_hash_alloc("md5", &client->internal->md5hash);
+  silc_hash_alloc("sha1", &client->internal->sha1hash);
 
   /* Initialize none cipher */
-  silc_cipher_alloc("none", &client->none_cipher);
+  silc_cipher_alloc("none", &client->internal->none_cipher);
 
   /* Initialize random number generator */
   client->rng = silc_rng_alloc();
@@ -113,11 +118,15 @@ int silc_client_init(SilcClient client)
   silc_client_protocols_register();
 
   /* Initialize the scheduler */
-  client->schedule = silc_schedule_init(client->params->task_max ?
-                                       client->params->task_max : 200);
+  client->schedule = 
+    silc_schedule_init(client->internal->params->task_max ?
+                      client->internal->params->task_max : 200);
   if (!client->schedule)
     return FALSE;
 
+  /* Register commands */
+  silc_client_commands_register(client);
+
   return TRUE;
 }
 
@@ -132,6 +141,7 @@ void silc_client_stop(SilcClient client)
   silc_schedule_uninit(client->schedule);
 
   silc_client_protocols_unregister();
+  silc_client_commands_unregister(client);
 
   SILC_LOG_DEBUG(("Client stopped"));
 }
@@ -199,16 +209,17 @@ SilcClientConnection silc_client_add_connection(SilcClient client,
   conn->ftp_sessions = silc_dlist_init();
 
   /* Add the connection to connections table */
-  for (i = 0; i < client->conns_count; i++)
-    if (client->conns && !client->conns[i]) {
-      client->conns[i] = conn;
+  for (i = 0; i < client->internal->conns_count; i++)
+    if (client->internal->conns && !client->internal->conns[i]) {
+      client->internal->conns[i] = conn;
       return conn;
     }
 
-  client->conns = silc_realloc(client->conns, sizeof(*client->conns)
-                              * (client->conns_count + 1));
-  client->conns[client->conns_count] = conn;
-  client->conns_count++;
+  client->internal->conns = 
+    silc_realloc(client->internal->conns, sizeof(*client->internal->conns)
+                * (client->internal->conns_count + 1));
+  client->internal->conns[client->internal->conns_count] = conn;
+  client->internal->conns_count++;
 
   return conn;
 }
@@ -219,8 +230,8 @@ void silc_client_del_connection(SilcClient client, SilcClientConnection conn)
 {
   int i;
 
-  for (i = 0; i < client->conns_count; i++)
-    if (client->conns[i] == conn) {
+  for (i = 0; i < client->internal->conns_count; i++)
+    if (client->internal->conns[i] == conn) {
 
       silc_idcache_free(conn->client_cache);
       silc_idcache_free(conn->channel_cache);
@@ -231,7 +242,7 @@ void silc_client_del_connection(SilcClient client, SilcClientConnection conn)
       silc_dlist_uninit(conn->ftp_sessions);
       silc_free(conn);
 
-      client->conns[i] = NULL;
+      client->internal->conns[i] = NULL;
     }
 }
 
@@ -243,24 +254,28 @@ void silc_client_add_socket(SilcClient client, SilcSocketConnection sock)
 {
   int i;
 
-  if (!client->sockets) {
-    client->sockets = silc_calloc(1, sizeof(*client->sockets));
-    client->sockets[0] = silc_socket_dup(sock);
-    client->sockets_count = 1;
+  if (!client->internal->sockets) {
+    client->internal->sockets = 
+      silc_calloc(1, sizeof(*client->internal->sockets));
+    client->internal->sockets[0] = silc_socket_dup(sock);
+    client->internal->sockets_count = 1;
     return;
   }
 
-  for (i = 0; i < client->sockets_count; i++) {
-    if (client->sockets[i] == NULL) {
-      client->sockets[i] = silc_socket_dup(sock);
+  for (i = 0; i < client->internal->sockets_count; i++) {
+    if (client->internal->sockets[i] == NULL) {
+      client->internal->sockets[i] = silc_socket_dup(sock);
       return;
     }
   }
 
-  client->sockets = silc_realloc(client->sockets, sizeof(*client->sockets) *
-                                (client->sockets_count + 1));
-  client->sockets[client->sockets_count] = silc_socket_dup(sock);
-  client->sockets_count++;
+  client->internal->sockets = 
+    silc_realloc(client->internal->sockets, 
+                sizeof(*client->internal->sockets) *
+                (client->internal->sockets_count + 1));
+  client->internal->sockets[client->internal->sockets_count] = 
+    silc_socket_dup(sock);
+  client->internal->sockets_count++;
 }
 
 /* Deletes listener socket from the listener sockets table. */
@@ -269,13 +284,13 @@ void silc_client_del_socket(SilcClient client, SilcSocketConnection sock)
 {
   int i;
 
-  if (!client->sockets)
+  if (!client->internal->sockets)
     return;
 
-  for (i = 0; i < client->sockets_count; i++) {
-    if (client->sockets[i] == sock) {
+  for (i = 0; i < client->internal->sockets_count; i++) {
+    if (client->internal->sockets[i] == sock) {
       silc_socket_free(sock);
-      client->sockets[i] = NULL;
+      client->internal->sockets[i] = NULL;
       return;
     }
   }
@@ -327,8 +342,8 @@ int silc_client_connect_to_server(SilcClient client, int port,
 
   conn = silc_client_add_connection(client, host, port, context);
 
-  client->ops->say(client, conn, SILC_CLIENT_MESSAGE_AUDIT, 
-                  "Connecting to port %d of server %s", port, host);
+  client->internal->ops->say(client, conn, SILC_CLIENT_MESSAGE_AUDIT, 
+                            "Connecting to port %d of server %s", port, host);
 
   /* Allocate internal context for connection process. This is
      needed as we are doing async connecting. */
@@ -391,8 +406,8 @@ bool silc_client_start_key_exchange(SilcClient client,
                      &protocol, (void *)proto_ctx,
                      silc_client_connect_to_server_second);
   if (!protocol) {
-    client->ops->say(client, conn, SILC_CLIENT_MESSAGE_ERROR,
-                    "Error: Could not start key exchange protocol");
+    client->internal->ops->say(client, conn, SILC_CLIENT_MESSAGE_ERROR,
+                              "Error: Could not start key exchange protocol");
     return FALSE;
   }
   conn->sock->protocol = protocol;
@@ -420,7 +435,7 @@ SILC_TASK_CALLBACK(silc_client_connect_failure)
     (SilcClientKEInternalContext *)context;
   SilcClient client = (SilcClient)ctx->client;
 
-  client->ops->connect(client, ctx->sock->user_data, FALSE);
+  client->internal->ops->connect(client, ctx->sock->user_data, FALSE);
   silc_free(ctx);
 }
 
@@ -442,12 +457,12 @@ SILC_TASK_CALLBACK(silc_client_connect_to_server_start)
   if (opt != 0) {
     if (ctx->tries < 2) {
       /* Connection failed but lets try again */
-      client->ops->say(client, conn, SILC_CLIENT_MESSAGE_ERROR,
-                      "Could not connect to server %s: %s",
-                      ctx->host, strerror(opt));
-      client->ops->say(client, conn, SILC_CLIENT_MESSAGE_AUDIT, 
-                      "Connecting to port %d of server %s resumed", 
-                      ctx->port, ctx->host);
+      client->internal->ops->say(client, conn, SILC_CLIENT_MESSAGE_ERROR,
+                                "Could not connect to server %s: %s",
+                                ctx->host, strerror(opt));
+      client->internal->ops->say(client, conn, SILC_CLIENT_MESSAGE_AUDIT, 
+                                "Connecting to port %d of server %s resumed", 
+                                ctx->port, ctx->host);
 
       /* Unregister old connection try */
       silc_schedule_unset_listen_fd(client->schedule, fd);
@@ -459,16 +474,16 @@ SILC_TASK_CALLBACK(silc_client_connect_to_server_start)
       ctx->tries++;
     } else {
       /* Connection failed and we won't try anymore */
-      client->ops->say(client, conn, SILC_CLIENT_MESSAGE_ERROR,
-                      "Could not connect to server %s: %s",
-                      ctx->host, strerror(opt));
+      client->internal->ops->say(client, conn, SILC_CLIENT_MESSAGE_ERROR,
+                                "Could not connect to server %s: %s",
+                                ctx->host, strerror(opt));
       silc_schedule_unset_listen_fd(client->schedule, fd);
       silc_net_close_connection(fd);
       silc_schedule_task_del(client->schedule, ctx->task);
       silc_free(ctx);
 
       /* Notify application of failure */
-      client->ops->connect(client, conn, FALSE);
+      client->internal->ops->connect(client, conn, FALSE);
       silc_client_del_connection(client, conn);
     }
     return;
@@ -480,7 +495,7 @@ SILC_TASK_CALLBACK(silc_client_connect_to_server_start)
 
   if (!silc_client_start_key_exchange(client, conn, fd)) {
     silc_net_close_connection(fd);
-    client->ops->connect(client, conn, FALSE);
+    client->internal->ops->connect(client, conn, FALSE);
   }
 }
 
@@ -549,15 +564,17 @@ SILC_TASK_CALLBACK(silc_client_connect_to_server_second)
   /* Resolve the authentication method to be used in this connection. The
      completion callback is called after the application has resolved
      the authentication method. */
-  client->ops->get_auth_method(client, sock->user_data, sock->hostname,
-                              sock->port, silc_client_resolve_auth_method,
-                              proto_ctx);
+  client->internal->ops->get_auth_method(client, sock->user_data, 
+                                        sock->hostname,
+                                        sock->port, 
+                                        silc_client_resolve_auth_method,
+                                        proto_ctx);
 }
 
 /* Authentication method resolving callback. Application calls this function
-   after we've called the client->ops->get_auth_method client operation
-   to resolve the authentication method. We will continue the executiong
-   of the protocol in this function. */
+   after we've called the client->internal->ops->get_auth_method 
+   client operation to resolve the authentication method. We will continue
+   the executiong of the protocol in this function. */
 
 void silc_client_resolve_auth_method(bool success,
                                     SilcProtocolAuthMeth auth_meth,
@@ -652,7 +669,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 = client->params->rekey_secs;
+  conn->rekey->timeout = client->internal->params->rekey_secs;
   conn->rekey->context = (void *)client;
   silc_schedule_task_add(client->schedule, conn->sock->sock, 
                         silc_client_rekey_callback,
@@ -771,14 +788,14 @@ SILC_TASK_CALLBACK_GLOBAL(silc_client_packet_process)
         close the connection */
       if (SILC_IS_DISCONNECTING(sock)) {
        if (sock == conn->sock && sock->type != SILC_SOCKET_TYPE_CLIENT)
-         client->ops->disconnect(client, conn);
+         client->internal->ops->disconnect(client, conn);
        silc_client_close_connection(client, sock, conn);
        return;
       }
       
       SILC_LOG_DEBUG(("EOF from connection %d", sock->sock));
       if (sock == conn->sock && sock->type != SILC_SOCKET_TYPE_CLIENT)
-       client->ops->disconnect(client, conn);
+       client->internal->ops->disconnect(client, conn);
       silc_client_close_connection(client, sock, conn);
       return;
     }
@@ -1401,7 +1418,8 @@ void silc_client_disconnected_by_server(SilcClient client,
 
   msg = silc_calloc(message->len + 1, sizeof(char));
   memcpy(msg, message->data, message->len);
-  client->ops->say(client, sock->user_data, SILC_CLIENT_MESSAGE_AUDIT, msg);
+  client->internal->ops->say(client, sock->user_data, 
+                            SILC_CLIENT_MESSAGE_AUDIT, msg);
   silc_free(msg);
 
   SILC_SET_DISCONNECTED(sock);
@@ -1424,7 +1442,8 @@ void silc_client_error_by_server(SilcClient client,
 
   msg = silc_calloc(message->len + 1, sizeof(char));
   memcpy(msg, message->data, message->len);
-  client->ops->say(client, sock->user_data, SILC_CLIENT_MESSAGE_AUDIT, msg);
+  client->internal->ops->say(client, sock->user_data, 
+                            SILC_CLIENT_MESSAGE_AUDIT, msg);
   silc_free(msg);
 }
 
@@ -1483,14 +1502,17 @@ void silc_client_receive_new_id(SilcClient client,
   if (connecting) {
     /* Issue INFO comqmand to fetch the real server name and server information
        and other stuff. */
+    silc_client_command_register(client, SILC_COMMAND_INFO, NULL, NULL,
+                                silc_client_command_reply_info_i, 0, 
+                                ++conn->cmd_ident);
     sidp = silc_id_payload_encode(conn->remote_id, SILC_ID_SERVER);
-    silc_client_send_command(client, conn, SILC_COMMAND_INFO,
-                            ++conn->cmd_ident, 1, 2, sidp->data, sidp->len);
+    silc_client_command_send(client, conn, SILC_COMMAND_INFO,
+                            conn->cmd_ident, 1, 2, sidp->data, sidp->len);
     silc_buffer_free(sidp);
 
     /* Notify application of successful connection. We do it here now that
        we've received the Client ID and are allowed to send traffic. */
-    client->ops->connect(client, conn, TRUE);
+    client->internal->ops->connect(client, conn, TRUE);
   }
 }
 
@@ -1618,8 +1640,8 @@ void silc_client_process_failure(SilcClient client,
       SILC_GET32_MSB(failure, packet->buffer->data);
 
     /* Notify application */
-    client->ops->failure(client, sock->user_data, sock->protocol,
-                        (void *)failure);
+    client->internal->ops->failure(client, sock->user_data, sock->protocol,
+                                  (void *)failure);
   }
 }
 
@@ -1803,6 +1825,7 @@ silc_client_request_authentication_method(SilcClient client,
   connauth->timeout =
     silc_schedule_task_add(client->schedule, conn->sock->sock, 
                           silc_client_request_authentication_method_timeout,
-                          conn, client->params->connauth_request_secs, 0,
+                          conn, 
+                          client->internal->params->connauth_request_secs, 0,
                           SILC_TASK_TIMEOUT, SILC_TASK_PRI_NORMAL);
 }
index d8bee7ad186bcf52747812ae46bb0382767be9d6..2b40557b91296c9053ec432884e15ca7e7e53cc0 100644 (file)
@@ -1,6 +1,6 @@
 /*
 
-  client.h
+  client.h 
 
   Author: Pekka Riikonen <priikone@silcnet.org>
 
@@ -8,9 +8,8 @@
 
   This program is free software; you can redistribute it and/or modify
   it under the terms of the GNU General Public License as published by
-  the Free Software Foundation; either version 2 of the License, or
-  (at your option) any later version.
-  
+  the Free Software Foundation; version 2 of the License.
+
   This program is distributed in the hope that it will be useful,
   but WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
@@ -23,6 +22,7 @@
 
 /* Forward declarations */
 typedef struct SilcClientStruct *SilcClient;
+typedef struct SilcClientInternalStruct *SilcClientInternal;
 typedef struct SilcClientConnectionStruct *SilcClientConnection;
 typedef struct SilcClientPingStruct SilcClientPing;
 typedef struct SilcClientAwayStruct SilcClientAway;
@@ -151,174 +151,28 @@ struct SilcClientConnectionStruct {
 
 /* Main client structure. */
 struct SilcClientStruct {
-  /*
-   * Public data. All the following pointers must be set by the allocator
-   * of this structure.
-   */
-
-  /* Users's username, hostname and realname. */
-  char *username;
-  char *hostname;
-  char *realname;
-
-  /* Private and public key of the user. */
-  SilcPKCS pkcs;
-  SilcPublicKey public_key;
-  SilcPrivateKey private_key;
-
-  /* Application specific user data pointer. Client library does not
-     touch this. */
-  void *application;
-
-  /*
-   * Private data. Following pointers are used internally by the client
-   * library and should be considered read-only fields.
-   */
-
-  /* All client operations that are implemented in the application. */
-  SilcClientOperations *ops;
-
-  /* Client Parameters */
-  SilcClientParams *params;
-
-  /* SILC client scheduler */
-  SilcSchedule schedule;
+  char *username;              /* Username, must be set by application */
+  char *hostname;              /* hostname, must be set by application */
+  char *realname;              /* Real name, must be set be application */
 
-  /* Table of connections in client. All the connection data is saved here. */
-  SilcClientConnection *conns;
-  uint32 conns_count;
+  SilcPublicKey public_key;    /* Public key of user, set by application */
+  SilcPrivateKey private_key;  /* Private key of user, set by application */
+  SilcPKCS pkcs;               /* PKCS allocated by application */
 
-  /* Table of listenning sockets in client.  Client can have listeners
-     (like key agreement protocol server) and those sockets are saved here.
-     This table is checked always if the connection object cannot be found
-     from the `conns' table. */
-  SilcSocketConnection *sockets;
-  uint32 sockets_count;
-
-  /* Generic cipher and hash objects. These can be used and referenced
-     by the application as well. */
-  SilcCipher none_cipher;
-  SilcHash md5hash;
-  SilcHash sha1hash;
-  SilcHmac md5hmac;
-  SilcHmac sha1hmac;
+  SilcSchedule schedule;       /* Scheduler, automatically allocated by
+                                  the client library. */
 
   /* Random Number Generator. Application should use this as its primary
      random number generator. */
   SilcRng rng;
 
-  /* Client version. Used to compare to remote host's version strings. */
-  char *silc_client_version;
+  /* Application specific user data pointer. Client library does not
+     touch this. This the context sent as argument to silc_client_alloc. */
+  void *application;
+
+  /* Internal data for client library. Application cannot access this
+     data at all. */
+  SilcClientInternal internal;
 };
 
-/* Macros */
-
-/* Registers generic task for file descriptor for reading from network and
-   writing to network. As being generic task the actual task is allocated 
-   only once and after that the same task applies to all registered fd's. */
-#define SILC_CLIENT_REGISTER_CONNECTION_FOR_IO(fd)     \
-do {                                                   \
-  silc_schedule_task_add(client->schedule, (fd),       \
-                        silc_client_packet_process,    \
-                        context, 0, 0,                 \
-                        SILC_TASK_GENERIC,             \
-                        SILC_TASK_PRI_NORMAL);         \
-} while(0)
-
-#define SILC_CLIENT_SET_CONNECTION_FOR_INPUT(s, fd)            \
-do {                                                           \
-  silc_schedule_set_listen_fd((s), (fd), SILC_TASK_READ);      \
-} while(0)
-     
-#define SILC_CLIENT_SET_CONNECTION_FOR_OUTPUT(s, fd)           \
-do {                                                           \
-  silc_schedule_set_listen_fd((s), (fd), (SILC_TASK_READ |     \
-                                         SILC_TASK_WRITE));    \
-} while(0)
-
-/* Finds socket connection object by file descriptor */
-#define SILC_CLIENT_GET_SOCK(__x, __fd, __sock)                \
-do {                                                   \
-  int __i;                                             \
-                                                       \
-  for (__i = 0; __i < (__x)->conns_count; __i++)       \
-    if ((__x)->conns[__i] &&                           \
-       (__x)->conns[__i]->sock->sock == (__fd))        \
-      break;                                           \
-                                                       \
-  if (__i >= (__x)->conns_count) {                     \
-    (__sock) = NULL;                                   \
-    for (__i = 0; __i < (__x)->sockets_count; __i++)   \
-      if ((__x)->sockets[__i] &&                       \
-         (__x)->sockets[__i]->sock == (__fd))          \
-        (__sock) = (__x)->sockets[__i];                        \
-  } else                                               \
-    (__sock) = (__x)->conns[__i]->sock;                        \
-} while(0)
-
-/* Check whether rekey protocol is active */
-#define SILC_CLIENT_IS_REKEY(sock)                                     \
-  (sock->protocol && sock->protocol->protocol &&                       \
-   sock->protocol->protocol->type == SILC_PROTOCOL_CLIENT_REKEY)
-
-/* Prototypes (some of the prototypes are defined in the silcapi.h) */
-
-void silc_client_packet_send(SilcClient client, 
-                            SilcSocketConnection sock,
-                            SilcPacketType type, 
-                            void *dst_id,
-                            SilcIdType dst_id_type,
-                            SilcCipher cipher,
-                            SilcHmac hmac,
-                            unsigned char *data, 
-                            uint32 data_len, 
-                            int force_send);
-void silc_client_disconnected_by_server(SilcClient client,
-                                       SilcSocketConnection sock,
-                                       SilcBuffer message);
-void silc_client_error_by_server(SilcClient client,
-                                SilcSocketConnection sock,
-                                SilcBuffer message);
-void silc_client_receive_new_id(SilcClient client,
-                               SilcSocketConnection sock,
-                               SilcIDPayload idp);
-SilcChannelEntry silc_client_new_channel_id(SilcClient client,
-                                           SilcSocketConnection sock,
-                                           char *channel_name,
-                                           uint32 mode, 
-                                           SilcIDPayload idp);
-void silc_client_save_channel_key(SilcClientConnection conn,
-                                 SilcBuffer key_payload, 
-                                 SilcChannelEntry channel);
-void silc_client_receive_channel_key(SilcClient client,
-                                    SilcSocketConnection sock,
-                                    SilcBuffer packet);
-void silc_client_channel_message(SilcClient client, 
-                                SilcSocketConnection sock, 
-                                SilcPacketContext *packet);
-void silc_client_remove_from_channels(SilcClient client,
-                                     SilcClientConnection conn,
-                                     SilcClientEntry client_entry);
-void silc_client_replace_from_channels(SilcClient client, 
-                                      SilcClientConnection conn,
-                                      SilcClientEntry old,
-                                      SilcClientEntry newclient);
-void silc_client_process_failure(SilcClient client,
-                                SilcSocketConnection sock,
-                                SilcPacketContext *packet);
-void silc_client_key_agreement(SilcClient client,
-                              SilcSocketConnection sock,
-                              SilcPacketContext *packet);
-void silc_client_notify_by_server(SilcClient client,
-                                 SilcSocketConnection sock,
-                                 SilcPacketContext *packet);
-void silc_client_private_message(SilcClient client, 
-                                SilcSocketConnection sock, 
-                                SilcPacketContext *packet);
-void silc_client_connection_auth_request(SilcClient client,
-                                        SilcSocketConnection sock,
-                                        SilcPacketContext *packet);
-void silc_client_ftp(SilcClient client,
-                    SilcSocketConnection sock,
-                    SilcPacketContext *packet);
 #endif
index fbe9686315a1d8291d5a62707ee13d9f86b66630..0f952373a55b747708aa10c6ea73b551526e5577 100644 (file)
@@ -95,7 +95,8 @@ void silc_client_send_channel_message(SilcClient client,
     for (i = 0; i < iv_len; i++) channel->iv[i] = 
                                   silc_rng_get_byte(client->rng);
   else
-    silc_hash_make(client->md5hash, channel->iv, iv_len, channel->iv);
+    silc_hash_make(client->internal->md5hash, channel->iv, iv_len, 
+                  channel->iv);
 
   /* Encode the channel payload. This also encrypts the message payload. */
   payload = silc_channel_message_payload_encode(flags, data_len, data, iv_len, 
@@ -181,9 +182,10 @@ static void silc_client_channel_message_cb(SilcClient client,
     message = silc_channel_message_get_data(res->payload, NULL);
     
     /* Pass the message to application */
-    client->ops->channel_message(client, conn, clients[0], channel,
-                                silc_channel_message_get_flags(res->payload),
-                                message);
+    client->internal->ops->channel_message(
+                              client, conn, clients[0], channel,
+                              silc_channel_message_get_flags(res->payload),
+                              message);
   }
 
  out:
@@ -297,9 +299,10 @@ void silc_client_channel_message(SilcClient client,
   message = silc_channel_message_get_data(payload, NULL);
 
   /* Pass the message to application */
-  client->ops->channel_message(client, conn, chu->client, channel,
-                              silc_channel_message_get_flags(payload),
-                              message);
+  client->internal->ops->channel_message(
+                                client, conn, chu->client, channel,
+                                silc_channel_message_get_flags(payload),
+                                message);
 
  out:
   if (id)
@@ -398,7 +401,9 @@ void silc_client_save_channel_key(SilcClientConnection conn,
   memcpy(channel->key, key, tmp_len);
 
   if (!silc_cipher_alloc(cipher, &channel->channel_key)) {
-    conn->client->ops->say(conn->client, conn, SILC_CLIENT_MESSAGE_AUDIT,
+    conn->client->internal->ops->say(
+                          conn->client, conn, 
+                          SILC_CLIENT_MESSAGE_AUDIT,
                           "Cannot talk to channel: unsupported cipher %s", 
                           cipher);
     goto out;
@@ -493,7 +498,7 @@ int silc_client_add_channel_private_key(SilcClient client,
   /* Produce the key material */
   keymat = silc_calloc(1, sizeof(*keymat));
   if (silc_ske_process_key_material_data(key, key_len, 16, 256, 16, 
-                                        client->md5hash, keymat) 
+                                        client->internal->md5hash, keymat) 
       != SILC_SKE_STATUS_OK)
     return FALSE;
 
index 1c20d625adbf8bc66d417de91b807ba05fa38e4d..f86ccbd5faaae973ec4d68a2eb7aad022524c994 100644 (file)
@@ -76,12 +76,12 @@ SILC_TASK_CALLBACK(silc_client_ftp_connected)
   if (opt != 0) {
     if (ctx->tries < 2) {
       /* Connection failed but lets try again */
-      client->ops->say(client, conn, SILC_CLIENT_MESSAGE_ERROR,
-                      "Could not connect to client %s: %s",
-                      ctx->host, strerror(opt));
-      client->ops->say(client, conn, SILC_CLIENT_MESSAGE_AUDIT, 
-                      "Connecting to port %d of client %s resumed", 
-                      ctx->port, ctx->host);
+      client->internal->ops->say(client, conn, SILC_CLIENT_MESSAGE_ERROR,
+                                "Could not connect to client %s: %s",
+                                ctx->host, strerror(opt));
+      client->internal->ops->say(client, conn, SILC_CLIENT_MESSAGE_AUDIT, 
+                                "Connecting to port %d of client %s resumed", 
+                                ctx->port, ctx->host);
 
       /* Unregister old connection try */
       silc_schedule_unset_listen_fd(client->schedule, fd);
@@ -93,9 +93,9 @@ SILC_TASK_CALLBACK(silc_client_ftp_connected)
       ctx->tries++;
     } else {
       /* Connection failed and we won't try anymore */
-      client->ops->say(client, conn, SILC_CLIENT_MESSAGE_ERROR,
-                      "Could not connect to client %s: %s",
-                      ctx->host, strerror(opt));
+      client->internal->ops->say(client, conn, SILC_CLIENT_MESSAGE_ERROR,
+                                "Could not connect to client %s: %s",
+                                ctx->host, strerror(opt));
       silc_schedule_unset_listen_fd(client->schedule, fd);
       silc_net_close_connection(fd);
       silc_schedule_task_del(client->schedule, ctx->task);
@@ -312,10 +312,11 @@ static void silc_client_ftp_open_handle(SilcSFTP sftp,
                               O_RDWR | O_CREAT | O_EXCL);
   if (session->fd < 0) {
     /* Call monitor callback */
-    session->client->ops->say(session->client, session->conn, 
-                             SILC_CLIENT_MESSAGE_ERROR, 
-                             "File `%s' open failed: %s", session->filepath,
-                             strerror(errno));
+    session->client->internal->ops->say(session->client, session->conn, 
+                                       SILC_CLIENT_MESSAGE_ERROR, 
+                                       "File `%s' open failed: %s", 
+                                       session->filepath,
+                                       strerror(errno));
 
     if (session->monitor)
       (*session->monitor)(session->client, session->conn,
@@ -918,9 +919,9 @@ silc_client_file_receive(SilcClient client,
     session->listener = silc_net_create_server(0, session->hostname);
     if (session->listener < 0) {
       SILC_LOG_DEBUG(("Could not create listener"));
-      client->ops->say(client, conn, SILC_CLIENT_MESSAGE_ERROR, 
-                      "Cannot create listener on %s: %s", 
-                      session->hostname, strerror(errno));
+      client->internal->ops->say(client, conn, SILC_CLIENT_MESSAGE_ERROR, 
+                                "Cannot create listener on %s: %s", 
+                                session->hostname, strerror(errno));
       return SILC_CLIENT_FILE_ERROR;
     }
     session->port = silc_net_get_local_port(session->listener);
@@ -1034,8 +1035,8 @@ static void silc_client_ftp_resolve_cb(SilcClient client,
     silc_dlist_add(conn->ftp_sessions, session);
 
     /* Let the application know */
-    client->ops->ftp(client, conn, client_entry,
-                    session->session_id, hostname, port);
+    client->internal->ops->ftp(client, conn, client_entry,
+                              session->session_id, hostname, port);
 
     if (hostname && port) {
       session->hostname = strdup(hostname);
index c9b950d42d55ab1e959aead7fe55f27daacd2036..f796cb108958ea1ca1395199d6e75f112d87b6cb 100644 (file)
@@ -1,16 +1,15 @@
 /*
 
-  client_internal.h
+  client_internal.h 
 
-  Author: Pekka Riikonen <priikone@poseidon.pspt.fi>
+  Author: Pekka Riikonen <priikone@silcnet.org>
 
   Copyright (C) 1997 - 2001 Pekka Riikonen
 
   This program is free software; you can redistribute it and/or modify
   it under the terms of the GNU General Public License as published by
-  the Free Software Foundation; either version 2 of the License, or
-  (at your option) any later version.
-  
+  the Free Software Foundation; version 2 of the License.
+
   This program is distributed in the hope that it will be useful,
   but WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
@@ -52,7 +51,90 @@ struct SilcClientAwayStruct {
   struct SilcClientAwayStruct *next;
 };
 
-/* Protypes */
+/* Internal context for the client->internal pointer in the SilcClient. */
+struct SilcClientInternalStruct {
+  /* All client operations that are implemented by the application. */
+  SilcClientOperations *ops;
+
+  /* Client Parameters */
+  SilcClientParams *params;
+
+  /* Table of connections in client. All the connection data is saved here. */
+  SilcClientConnection *conns;
+  uint32 conns_count;
+
+  /* Table of listenning sockets in client.  Client can have listeners
+     (like key agreement protocol server) and those sockets are saved here.
+     This table is checked always if the connection object cannot be found
+     from the `conns' table. */
+  SilcSocketConnection *sockets;
+  uint32 sockets_count;
+
+  /* Registered commands */
+  SilcList commands;
+
+  /* Generic cipher and hash objects. */
+  SilcCipher none_cipher;
+  SilcHash md5hash;
+  SilcHash sha1hash;
+  SilcHmac md5hmac;
+  SilcHmac sha1hmac;
+
+  /* Client version. Used to compare to remote host's version strings. */
+  char *silc_client_version;
+};
+
+/* Macros */
+
+/* Registers generic task for file descriptor for reading from network and
+   writing to network. As being generic task the actual task is allocated 
+   only once and after that the same task applies to all registered fd's. */
+#define SILC_CLIENT_REGISTER_CONNECTION_FOR_IO(fd)     \
+do {                                                   \
+  silc_schedule_task_add(client->schedule, (fd),       \
+                        silc_client_packet_process,    \
+                        context, 0, 0,                 \
+                        SILC_TASK_GENERIC,             \
+                        SILC_TASK_PRI_NORMAL);         \
+} while(0)
+
+#define SILC_CLIENT_SET_CONNECTION_FOR_INPUT(s, fd)            \
+do {                                                           \
+  silc_schedule_set_listen_fd((s), (fd), SILC_TASK_READ);      \
+} while(0)
+     
+#define SILC_CLIENT_SET_CONNECTION_FOR_OUTPUT(s, fd)           \
+do {                                                           \
+  silc_schedule_set_listen_fd((s), (fd), (SILC_TASK_READ |     \
+                                         SILC_TASK_WRITE));    \
+} while(0)
+
+/* Finds socket connection object by file descriptor */                \
+#define SILC_CLIENT_GET_SOCK(__x, __fd, __sock)                        \
+do {                                                           \
+  int __i;                                                     \
+                                                               \
+  for (__i = 0; __i < (__x)->internal->conns_count; __i++)     \
+    if ((__x)->internal->conns[__i] &&                         \
+       (__x)->internal->conns[__i]->sock->sock == (__fd))      \
+      break;                                                   \
+                                                               \
+  if (__i >= (__x)->internal->conns_count) {                   \
+    (__sock) = NULL;                                           \
+    for (__i = 0; __i < (__x)->internal->sockets_count; __i++) \
+      if ((__x)->internal->sockets[__i] &&                     \
+         (__x)->internal->sockets[__i]->sock == (__fd))        \
+        (__sock) = (__x)->internal->sockets[__i];              \
+  } else                                                       \
+    (__sock) = (__x)->internal->conns[__i]->sock;              \
+} while(0)
+
+/* Check whether rekey protocol is active */
+#define SILC_CLIENT_IS_REKEY(sock)                                     \
+  (sock->protocol && sock->protocol->protocol &&                       \
+   sock->protocol->protocol->type == SILC_PROTOCOL_CLIENT_REKEY)
+
+/* Prototypes */
 
 SILC_TASK_CALLBACK_GLOBAL(silc_client_packet_process);
 int silc_client_packet_send_real(SilcClient client,
@@ -63,5 +145,63 @@ void silc_client_ftp_free_sessions(SilcClient client,
 void silc_client_ftp_session_free(SilcClientFtpSession session);
 void silc_client_ftp_session_free_client(SilcClientConnection conn,
                                         SilcClientEntry client_entry);
+void silc_client_packet_send(SilcClient client, 
+                            SilcSocketConnection sock,
+                            SilcPacketType type, 
+                            void *dst_id,
+                            SilcIdType dst_id_type,
+                            SilcCipher cipher,
+                            SilcHmac hmac,
+                            unsigned char *data, 
+                            uint32 data_len, 
+                            int force_send);
+void silc_client_disconnected_by_server(SilcClient client,
+                                       SilcSocketConnection sock,
+                                       SilcBuffer message);
+void silc_client_error_by_server(SilcClient client,
+                                SilcSocketConnection sock,
+                                SilcBuffer message);
+void silc_client_receive_new_id(SilcClient client,
+                               SilcSocketConnection sock,
+                               SilcIDPayload idp);
+SilcChannelEntry silc_client_new_channel_id(SilcClient client,
+                                           SilcSocketConnection sock,
+                                           char *channel_name,
+                                           uint32 mode, 
+                                           SilcIDPayload idp);
+void silc_client_save_channel_key(SilcClientConnection conn,
+                                 SilcBuffer key_payload, 
+                                 SilcChannelEntry channel);
+void silc_client_receive_channel_key(SilcClient client,
+                                    SilcSocketConnection sock,
+                                    SilcBuffer packet);
+void silc_client_channel_message(SilcClient client, 
+                                SilcSocketConnection sock, 
+                                SilcPacketContext *packet);
+void silc_client_remove_from_channels(SilcClient client,
+                                     SilcClientConnection conn,
+                                     SilcClientEntry client_entry);
+void silc_client_replace_from_channels(SilcClient client, 
+                                      SilcClientConnection conn,
+                                      SilcClientEntry old,
+                                      SilcClientEntry newclient);
+void silc_client_process_failure(SilcClient client,
+                                SilcSocketConnection sock,
+                                SilcPacketContext *packet);
+void silc_client_key_agreement(SilcClient client,
+                              SilcSocketConnection sock,
+                              SilcPacketContext *packet);
+void silc_client_notify_by_server(SilcClient client,
+                                 SilcSocketConnection sock,
+                                 SilcPacketContext *packet);
+void silc_client_private_message(SilcClient client, 
+                                SilcSocketConnection sock, 
+                                SilcPacketContext *packet);
+void silc_client_connection_auth_request(SilcClient client,
+                                        SilcSocketConnection sock,
+                                        SilcPacketContext *packet);
+void silc_client_ftp(SilcClient client,
+                    SilcSocketConnection sock,
+                    SilcPacketContext *packet);
 
 #endif
index 37f980034999bb43e7600f318da11237260bc0f1..2c5e20880c3ef617bdbb80d858d959d42c9892cb 100644 (file)
@@ -152,9 +152,9 @@ SILC_TASK_CALLBACK(silc_client_process_key_agreement)
 
   sock = silc_net_accept_connection(ke->fd);
   if (sock < 0) {
-    client->ops->say(client, conn, SILC_CLIENT_MESSAGE_AUDIT,
-                    "Could not accept key agreement connection: ", 
-                    strerror(errno));
+    client->internal->ops->say(client, conn, SILC_CLIENT_MESSAGE_AUDIT,
+                              "Could not accept key agreement connection: ", 
+                              strerror(errno));
     ke->client_entry->ke = NULL;
     ke->completion(ke->client, ke->conn, ke->client_entry, 
                   SILC_KEY_AGREEMENT_ERROR, NULL, ke->context);
@@ -180,8 +180,8 @@ SILC_TASK_CALLBACK(silc_client_process_key_agreement)
   /* Perform name and address lookups for the remote host. */
   silc_net_check_host_by_sock(sock, &newsocket->hostname, &newsocket->ip);
   if (!newsocket->hostname && !newsocket->ip) {
-    client->ops->say(client, conn, SILC_CLIENT_MESSAGE_AUDIT, 
-                    "Could not resolve the remote IP or hostname");
+    client->internal->ops->say(client, conn, SILC_CLIENT_MESSAGE_AUDIT, 
+                              "Could not resolve the remote IP or hostname");
     ke->client_entry->ke = NULL;
     ke->completion(ke->client, ke->conn, ke->client_entry, 
                   SILC_KEY_AGREEMENT_ERROR, NULL, ke->context);
@@ -324,9 +324,10 @@ void silc_client_send_key_agreement(SilcClient client,
       ke->fd = silc_net_create_server(port, hostname);
 
     if (ke->fd < 0) {
-      client->ops->say(client, conn, SILC_CLIENT_MESSAGE_ERROR, 
-                      "Cannot create listener on %s on port %d: %s", 
-                      (bindhost) ? bindhost:hostname, port, strerror(errno));
+      client->internal->ops->say(
+                    client, conn, SILC_CLIENT_MESSAGE_ERROR, 
+                    "Cannot create listener on %s on port %d: %s", 
+                    (bindhost) ? bindhost:hostname, port, strerror(errno));
       completion(client, conn, client_entry, SILC_KEY_AGREEMENT_FAILURE,
                 NULL, context);
       silc_free(ke);
@@ -437,12 +438,12 @@ SILC_TASK_CALLBACK(silc_client_perform_key_agreement_start)
   if (opt != 0) {
     if (ctx->tries < 2) {
       /* Connection failed but lets try again */
-      client->ops->say(client, conn, SILC_CLIENT_MESSAGE_ERROR,
-                      "Could not connect to client %s: %s",
-                      ctx->host, strerror(opt));
-      client->ops->say(client, conn, SILC_CLIENT_MESSAGE_AUDIT, 
-                      "Connecting to port %d of client %s resumed", 
-                      ctx->port, ctx->host);
+      client->internal->ops->say(client, conn, SILC_CLIENT_MESSAGE_ERROR,
+                                "Could not connect to client %s: %s",
+                                ctx->host, strerror(opt));
+      client->internal->ops->say(client, conn, SILC_CLIENT_MESSAGE_AUDIT, 
+                                "Connecting to port %d of client %s resumed", 
+                                ctx->port, ctx->host);
 
       /* Unregister old connection try */
       silc_schedule_unset_listen_fd(client->schedule, fd);
@@ -454,9 +455,9 @@ SILC_TASK_CALLBACK(silc_client_perform_key_agreement_start)
       ctx->tries++;
     } else {
       /* Connection failed and we won't try anymore */
-      client->ops->say(client, conn, SILC_CLIENT_MESSAGE_ERROR,
-                      "Could not connect to client %s: %s",
-                      ctx->host, strerror(opt));
+      client->internal->ops->say(client, conn, SILC_CLIENT_MESSAGE_ERROR,
+                                "Could not connect to client %s: %s",
+                                ctx->host, strerror(opt));
       silc_schedule_unset_listen_fd(client->schedule, fd);
       silc_net_close_connection(fd);
       silc_schedule_task_del(client->schedule, ctx->task);
@@ -661,7 +662,8 @@ silc_client_key_agreement_resolve_cb(SilcClient client,
     goto out;
 
   /* Call the key_agreement client operation */
-  ret = client->ops->key_agreement(client, conn, clients[0], 
+  ret = client->internal->ops->key_agreement(
+                                  client, conn, clients[0], 
                                   silc_key_agreement_get_hostname(payload),
                                   silc_key_agreement_get_port(payload),
                                   &completion, &completion_context);
index b4d4a78e8e166228f6c0b6e4f80f49099f3438cd..49c315c51fc46e5cddd0c3230447885d822776c4 100644 (file)
@@ -78,7 +78,10 @@ static void silc_client_notify_by_server_resolve(SilcClient client,
   res->context = client;
   res->sock = silc_socket_dup(conn->sock);
 
-  silc_client_send_command(client, conn, SILC_COMMAND_WHOIS, ++conn->cmd_ident,
+  silc_client_command_register(client, SILC_COMMAND_WHOIS, NULL, NULL,
+                              silc_client_command_reply_whois_i, 0,
+                              ++conn->cmd_ident);
+  silc_client_command_send(client, conn, SILC_COMMAND_WHOIS, conn->cmd_ident,
                           1, 3, idp->data, idp->len);
   silc_client_command_pending(conn, SILC_COMMAND_WHOIS, conn->cmd_ident,
                              silc_client_notify_by_server_destructor,
@@ -125,8 +128,8 @@ void silc_client_notify_by_server(SilcClient client,
   switch(type) {
   case SILC_NOTIFY_TYPE_NONE:
     /* Notify application */
-    client->ops->notify(client, conn, type, 
-                       silc_argument_get_arg_type(args, 1, NULL));
+    client->internal->ops->notify(client, conn, type, 
+                                 silc_argument_get_arg_type(args, 1, NULL));
     break;
 
   case SILC_NOTIFY_TYPE_INVITE:
@@ -174,7 +177,8 @@ void silc_client_notify_by_server(SilcClient client,
       goto out;
 
     /* Notify application */
-    client->ops->notify(client, conn, type, channel, tmp, client_entry);
+    client->internal->ops->notify(client, conn, type, channel, tmp, 
+                                 client_entry);
     break;
 
   case SILC_NOTIFY_TYPE_JOIN:
@@ -241,7 +245,7 @@ void silc_client_notify_by_server(SilcClient client,
     /* Notify application. The channel entry is sent last as this notify
        is for channel but application don't know it from the arguments
        sent by server. */
-    client->ops->notify(client, conn, type, client_entry, channel);
+    client->internal->ops->notify(client, conn, type, client_entry, channel);
     break;
 
   case SILC_NOTIFY_TYPE_LEAVE:
@@ -291,7 +295,7 @@ void silc_client_notify_by_server(SilcClient client,
     /* Notify application. The channel entry is sent last as this notify
        is for channel but application don't know it from the arguments
        sent by server. */
-    client->ops->notify(client, conn, type, client_entry, channel);
+    client->internal->ops->notify(client, conn, type, client_entry, channel);
     break;
 
   case SILC_NOTIFY_TYPE_SIGNOFF:
@@ -328,7 +332,7 @@ void silc_client_notify_by_server(SilcClient client,
       tmp = NULL;
 
     /* Notify application */
-    client->ops->notify(client, conn, type, client_entry, tmp);
+    client->internal->ops->notify(client, conn, type, client_entry, tmp);
 
     /* Free data */
     silc_client_del_client_entry(client, conn, client_entry);
@@ -418,8 +422,9 @@ void silc_client_notify_by_server(SilcClient client,
     /* Notify application. The channel entry is sent last as this notify
        is for channel but application don't know it from the arguments
        sent by server. */
-    client->ops->notify(client, conn, type, silc_id_payload_get_type(idp),
-                       client_entry, tmp, channel);
+    client->internal->ops->notify(client, conn, type, 
+                                 silc_id_payload_get_type(idp),
+                                 client_entry, tmp, channel);
 
     silc_id_payload_free(idp);
     break;
@@ -482,7 +487,8 @@ void silc_client_notify_by_server(SilcClient client,
                                      client_entry2);
 
     /* Notify application */
-    client->ops->notify(client, conn, type, client_entry, client_entry2);
+    client->internal->ops->notify(client, conn, type, 
+                                 client_entry, client_entry2);
 
     /* Free data */
     silc_client_del_client_entry(client, conn, client_entry);
@@ -584,8 +590,9 @@ void silc_client_notify_by_server(SilcClient client,
     /* Notify application. The channel entry is sent last as this notify
        is for channel but application don't know it from the arguments
        sent by server. */
-    client->ops->notify(client, conn, type, silc_id_payload_get_type(idp), 
-                       client_entry, mode, NULL, tmp, channel);
+    client->internal->ops->notify(client, conn, type, 
+                                 silc_id_payload_get_type(idp), 
+                                 client_entry, mode, NULL, tmp, channel);
 
     silc_id_payload_free(idp);
     break;
@@ -659,8 +666,9 @@ void silc_client_notify_by_server(SilcClient client,
     /* Notify application. The channel entry is sent last as this notify
        is for channel but application don't know it from the arguments
        sent by server. */
-    client->ops->notify(client, conn, type, client_entry, mode, 
-                       client_entry2, channel);
+    client->internal->ops->notify(client, conn, type, 
+                                 client_entry, mode, 
+                                 client_entry2, channel);
     break;
 
   case SILC_NOTIFY_TYPE_MOTD:
@@ -676,7 +684,7 @@ void silc_client_notify_by_server(SilcClient client,
       goto out;
     
     /* Notify application */
-    client->ops->notify(client, conn, type, tmp);
+    client->internal->ops->notify(client, conn, type, tmp);
     break;
 
   case SILC_NOTIFY_TYPE_CHANNEL_CHANGE:
@@ -727,7 +735,7 @@ void silc_client_notify_by_server(SilcClient client,
                     channel->id, channel, 0, NULL);
 
     /* Notify application */
-    client->ops->notify(client, conn, type, channel, channel);
+    client->internal->ops->notify(client, conn, type, channel, channel);
     break;
 
   case SILC_NOTIFY_TYPE_KICKED:
@@ -787,8 +795,8 @@ void silc_client_notify_by_server(SilcClient client,
     /* Notify application. The channel entry is sent last as this notify
        is for channel but application don't know it from the arguments
        sent by server. */
-    client->ops->notify(client, conn, type, client_entry, tmp, 
-                       client_entry2, channel);
+    client->internal->ops->notify(client, conn, type, client_entry, tmp, 
+                                 client_entry2, channel);
 
     /* If I was kicked from channel, remove the channel */
     if (client_entry == conn->local_entry) {
@@ -828,7 +836,7 @@ void silc_client_notify_by_server(SilcClient client,
     tmp = silc_argument_get_arg_type(args, 2, &tmp_len);
 
     /* Notify application. */
-    client->ops->notify(client, conn, type, client_entry, tmp);
+    client->internal->ops->notify(client, conn, type, client_entry, tmp);
 
     if (client_entry != conn->local_entry) {
       /* Remove client from all channels */
@@ -874,7 +882,8 @@ void silc_client_notify_by_server(SilcClient client,
       /* Notify application. We don't keep server entries so the server
         entry is returned as NULL. The client's are returned as array
         of SilcClientEntry pointers. */
-      client->ops->notify(client, conn, type, NULL, clients, clients_count);
+      client->internal->ops->notify(client, conn, type, NULL, 
+                                   clients, clients_count);
 
       for (i = 0; i < clients_count; i++) {
        /* Remove client from all channels */
index 331e1d6990cba16b376e7c500f3b4b8a5f187042..45b785e211b495e111670542467775917a80b081 100644 (file)
@@ -197,9 +197,10 @@ void silc_client_private_message(SilcClient client,
   flags = silc_private_message_get_flags(payload);
 
   /* Pass the private message to application */
-  client->ops->private_message(client, conn, remote_client, flags,
-                              silc_private_message_get_message(payload, 
-                                                               NULL));
+  client->internal->ops->private_message(
+                                client, conn, remote_client, flags,
+                                silc_private_message_get_message(payload, 
+                                                                 NULL));
 
   /* See if we are away (gone). If we are away we will reply to the
      sender with the set away message. */
@@ -256,14 +257,15 @@ static void silc_client_private_message_key_cb(SilcClient client,
     goto out;
 
   /* Print some info for application */
-  client->ops->say(client, conn, SILC_CLIENT_MESSAGE_AUDIT, 
-                  "Received private message key from %s%s%s %s%s%s", 
-                  clients[0]->nickname,
-                  clients[0]->server ? "@" : "",
-                  clients[0]->server ? clients[0]->server : "",
-                  clients[0]->username ? "(" : "",
-                  clients[0]->username ? clients[0]->username : "",
-                  clients[0]->username ? ")" : "");
+  client->internal->ops->say(
+                    client, conn, SILC_CLIENT_MESSAGE_AUDIT, 
+                    "Received private message key from %s%s%s %s%s%s", 
+                    clients[0]->nickname,
+                    clients[0]->server ? "@" : "",
+                    clients[0]->server ? clients[0]->server : "",
+                    clients[0]->username ? "(" : "",
+                    clients[0]->username ? clients[0]->username : "",
+                    clients[0]->username ? ")" : "");
 
  out:
   silc_packet_context_free(packet);
@@ -357,7 +359,7 @@ int silc_client_add_private_message_key(SilcClient client,
   /* Produce the key material as the protocol defines */
   keymat = silc_calloc(1, sizeof(*keymat));
   if (silc_ske_process_key_material_data(key, key_len, 16, 256, 16, 
-                                        client->md5hash, keymat) 
+                                        client->internal->md5hash, keymat) 
       != SILC_SKE_STATUS_OK)
     return FALSE;
 
index d1cf1f698934d8acacba27268caa01a44bd1bf53..cf3fc46e4f34072ba8f34e0018929882d81a3586 100644 (file)
@@ -1,16 +1,15 @@
 /*
 
-  command.c
+  command.c 
 
-  Author: Pekka Riikonen <priikone@poseidon.pspt.fi>
+  Author: Pekka Riikonen <priikone@silcnet.org>
 
   Copyright (C) 1997 - 2001 Pekka Riikonen
 
   This program is free software; you can redistribute it and/or modify
   it under the terms of the GNU General Public License as published by
-  the Free Software Foundation; either version 2 of the License, or
-  (at your option) any later version.
-  
+  the Free Software Foundation; version 2 of the License.
+
   This program is distributed in the hope that it will be useful,
   but WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 #include "clientlibincludes.h"
 #include "client_internal.h"
 
-/* Client command list. */
-SilcClientCommand silc_command_list[] =
-{
-  SILC_CLIENT_CMD(whois, WHOIS, "WHOIS", SILC_CF_LAG | SILC_CF_REG, 3),
-  SILC_CLIENT_CMD(whowas, WHOWAS, "WHOWAS", SILC_CF_LAG | SILC_CF_REG, 3),
-  SILC_CLIENT_CMD(identify, IDENTIFY, "IDENTIFY", 
-                 SILC_CF_LAG | SILC_CF_REG, 3),
-  SILC_CLIENT_CMD(nick, NICK, "NICK", SILC_CF_LAG | SILC_CF_REG, 2),
-  SILC_CLIENT_CMD(list, LIST, "LIST", SILC_CF_LAG | SILC_CF_REG, 2),
-  SILC_CLIENT_CMD(topic, TOPIC, "TOPIC", SILC_CF_LAG | SILC_CF_REG, 3),
-  SILC_CLIENT_CMD(invite, INVITE, "INVITE", SILC_CF_LAG | SILC_CF_REG, 3),
-  SILC_CLIENT_CMD(quit, QUIT, "QUIT", SILC_CF_LAG | SILC_CF_REG, 2),
-  SILC_CLIENT_CMD(kill, KILL, "KILL", 
-                 SILC_CF_LAG | SILC_CF_REG | SILC_CF_OPER, 3),
-  SILC_CLIENT_CMD(info, INFO, "INFO", SILC_CF_LAG | SILC_CF_REG, 2),
-  SILC_CLIENT_CMD(connect, CONNECT, "CONNECT",
-                 SILC_CF_LAG | SILC_CF_REG | SILC_CF_OPER, 3),
-  SILC_CLIENT_CMD(ping, PING, "PING", SILC_CF_LAG | SILC_CF_REG, 2),
-  SILC_CLIENT_CMD(oper, OPER, "OPER",
-                 SILC_CF_LAG | SILC_CF_REG | SILC_CF_OPER, 2),
-  SILC_CLIENT_CMD(join, JOIN, "JOIN", SILC_CF_LAG | SILC_CF_REG, 9),
-  SILC_CLIENT_CMD(motd, MOTD, "MOTD", SILC_CF_LAG | SILC_CF_REG, 2),
-  SILC_CLIENT_CMD(umode, UMODE, "UMODE", SILC_CF_LAG | SILC_CF_REG, 2),
-  SILC_CLIENT_CMD(cmode, CMODE, "CMODE", SILC_CF_LAG | SILC_CF_REG, 4),
-  SILC_CLIENT_CMD(cumode, CUMODE, "CUMODE", SILC_CF_LAG | SILC_CF_REG, 5),
-  SILC_CLIENT_CMD(kick, KICK, "KICK", SILC_CF_LAG | SILC_CF_REG, 4),
-  SILC_CLIENT_CMD(ban, BAN, "BAN", SILC_CF_LAG | SILC_CF_REG, 3),
-  SILC_CLIENT_CMD(close, CLOSE, "CLOSE",
-                 SILC_CF_LAG | SILC_CF_REG | SILC_CF_OPER, 3),
-  SILC_CLIENT_CMD(shutdown, SHUTDOWN, "SHUTDOWN",
-                 SILC_CF_LAG | SILC_CF_REG | SILC_CF_OPER, 1),
-  SILC_CLIENT_CMD(silcoper, SILCOPER, "SILCOPER",
-                 SILC_CF_LAG | SILC_CF_REG | SILC_CF_SILC_OPER, 3),
-  SILC_CLIENT_CMD(leave, LEAVE, "LEAVE", SILC_CF_LAG | SILC_CF_REG, 2),
-  SILC_CLIENT_CMD(users, USERS, "USERS", SILC_CF_LAG | SILC_CF_REG, 2),
-  SILC_CLIENT_CMD(getkey, GETKEY, "GETKEY", SILC_CF_LAG | SILC_CF_REG, 2),
-
-  { NULL, 0, NULL, 0, 0 },
-};
-
 #define SILC_NOT_CONNECTED(x, c) \
-  x->ops->say((x), (c), SILC_CLIENT_MESSAGE_ERROR, \
+  x->internal->ops->say((x), (c), SILC_CLIENT_MESSAGE_ERROR, \
           "You are not connected to a server, use /SERVER to connect");
 
 /* Command operation that is called at the end of all commands. 
    Usage: COMMAND; */
-#define COMMAND cmd->client->ops->command(cmd->client, cmd->conn, \
+#define COMMAND cmd->client->internal->ops->command(cmd->client, cmd->conn, \
   cmd, TRUE, cmd->command->cmd)
 
 /* Error to application. Usage: COMMAND_ERROR; */
-#define COMMAND_ERROR cmd->client->ops->command(cmd->client, cmd->conn, \
-  cmd, FALSE, cmd->command->cmd)
+#define COMMAND_ERROR cmd->client->internal->ops->command(cmd->client, \
+  cmd->conn, cmd, FALSE, cmd->command->cmd)
 
 /* Generic function to send any command. The arguments must be sent already
    encoded into correct form and in correct order. */
 
-void silc_client_send_command(SilcClient client, SilcClientConnection conn,
+void silc_client_command_send(SilcClient client, SilcClientConnection conn,
                              SilcCommand command, uint16 ident,
                              uint32 argc, ...)
 {
@@ -97,18 +56,32 @@ void silc_client_send_command(SilcClient client, SilcClientConnection conn,
 /* Finds and returns a pointer to the command list. Return NULL if the
    command is not found. */
 
-SilcClientCommand *silc_client_command_find(const char *name)
+SilcClientCommand silc_client_command_find(SilcClient client,
+                                          const char *name)
 {
-  SilcClientCommand *cmd;
+  SilcClientCommand cmd;
 
-  for (cmd = silc_command_list; cmd->name; cmd++) {
-    if (!strcmp(cmd->name, name))
+  silc_list_start(client->internal->commands);
+  while ((cmd = silc_list_get(client->internal->commands)) != SILC_LIST_END) {
+    if (cmd->name && !strcmp(cmd->name, name))
       return cmd;
   }
 
   return NULL;
 }
 
+/* Calls the command (executes it).  Application can call this after
+   it has allocated the SilcClientCommandContext with the function
+   silc_client_command_alloc and found the command from the client
+   library by calling silc_client_command_find.  This will execute
+   the command. */
+
+void silc_client_command_call(SilcClientCommand command, 
+                             SilcClientCommandContext cmd)
+{
+  (*command->command)((void *)cmd, NULL);
+}
+
 /* Add new pending command to be executed when reply to a command has been
    received.  The `reply_cmd' is the command that will call the `callback'
    with `context' when reply has been received.  If `ident is non-zero
@@ -176,7 +149,7 @@ int silc_client_command_pending_check(SilcClientConnection conn,
 
 /* Allocate Command Context */
 
-SilcClientCommandContext silc_client_command_alloc()
+SilcClientCommandContext silc_client_command_alloc(void)
 {
   SilcClientCommandContext ctx = silc_calloc(1, sizeof(*ctx));
   ctx->users++;
@@ -237,7 +210,7 @@ SILC_CLIENT_CMD_FUNC(whois)
   /* Given without arguments fetches client's own information */
   if (cmd->argc < 2) {
     buffer = silc_id_payload_encode(cmd->conn->local_id, SILC_ID_CLIENT);
-    silc_client_send_command(cmd->client, cmd->conn, SILC_COMMAND_WHOIS, 
+    silc_client_command_send(cmd->client, cmd->conn, SILC_COMMAND_WHOIS, 
                             ++conn->cmd_ident,
                             1, 3, buffer->data, buffer->len);
     silc_buffer_free(buffer);
@@ -279,7 +252,7 @@ SILC_CLIENT_CMD_FUNC(whowas)
   }
 
   if (cmd->argc < 2 || cmd->argc > 3) {
-    cmd->client->ops->say(cmd->client, conn, SILC_CLIENT_MESSAGE_INFO, 
+    cmd->client->internal->ops->say(cmd->client, conn, SILC_CLIENT_MESSAGE_INFO, 
             "Usage: /WHOWAS <nickname>[@<server>] [<count>]");
     COMMAND_ERROR;
     goto out;
@@ -305,7 +278,10 @@ SILC_CLIENT_CMD_FUNC(whowas)
 }
 
 /* Command IDENTIFY. This command is used to query information about 
-   specific user, especially ID's. */
+   specific user, especially ID's. 
+
+   NOTE: This command is used only internally by the client library
+   and application MUST NOT call this command directly. */
 
 SILC_CLIENT_CMD_FUNC(identify)
 {
@@ -315,14 +291,11 @@ SILC_CLIENT_CMD_FUNC(identify)
 
   if (!cmd->conn) {
     SILC_NOT_CONNECTED(cmd->client, cmd->conn);
-    COMMAND_ERROR;
     goto out;
   }
 
-  if (cmd->argc < 2 || cmd->argc > 3) {
-    COMMAND_ERROR;
+  if (cmd->argc < 2 || cmd->argc > 3)
     goto out;
-  }
 
   if (cmd->argc == 2)
     buffer = silc_command_payload_encode_va(SILC_COMMAND_IDENTIFY, 
@@ -342,9 +315,6 @@ SILC_CLIENT_CMD_FUNC(identify)
                          buffer->data, buffer->len, TRUE);
   silc_buffer_free(buffer);
 
-  /* Notify application */
-  COMMAND;
-
  out:
   silc_client_command_free(cmd);
 }
@@ -369,6 +339,7 @@ SILC_CLIENT_CMD_FUNC(nick_change)
       silc_free(conn->nickname);
     conn->nickname = strdup(cmd->argv[1]);
     conn->local_entry->nickname = conn->nickname;
+    silc_client_nickname_format(cmd->client, conn, conn->local_entry);
     silc_idcache_add(conn->client_cache, strdup(cmd->argv[1]), 
                     conn->local_entry->id, conn->local_entry, 0, NULL);
     COMMAND;
@@ -395,7 +366,7 @@ SILC_CLIENT_CMD_FUNC(nick)
   }
 
   if (cmd->argc < 2) {
-    cmd->client->ops->say(cmd->client, conn, SILC_CLIENT_MESSAGE_INFO, 
+    cmd->client->internal->ops->say(cmd->client, conn, SILC_CLIENT_MESSAGE_INFO, 
                          "Usage: /NICK <nickname>");
     COMMAND_ERROR;
     goto out;
@@ -407,11 +378,11 @@ SILC_CLIENT_CMD_FUNC(nick)
   /* Show current nickname */
   if (cmd->argc < 2) {
     if (cmd->conn) {
-      cmd->client->ops->say(cmd->client, conn, SILC_CLIENT_MESSAGE_INFO, 
+      cmd->client->internal->ops->say(cmd->client, conn, SILC_CLIENT_MESSAGE_INFO, 
                            "Your nickname is %s on server %s", 
                            conn->nickname, conn->remote_host);
     } else {
-      cmd->client->ops->say(cmd->client, conn, SILC_CLIENT_MESSAGE_INFO, 
+      cmd->client->internal->ops->say(cmd->client, conn, SILC_CLIENT_MESSAGE_INFO, 
                            "Your nickname is %s", conn->nickname);
     }
 
@@ -514,7 +485,7 @@ SILC_CLIENT_CMD_FUNC(topic)
   }
 
   if (cmd->argc < 2 || cmd->argc > 3) {
-    cmd->client->ops->say(cmd->client, conn, SILC_CLIENT_MESSAGE_INFO,
+    cmd->client->internal->ops->say(cmd->client, conn, SILC_CLIENT_MESSAGE_INFO,
                          "Usage: /TOPIC <channel> [<topic>]");
     COMMAND_ERROR;
     goto out;
@@ -522,7 +493,7 @@ SILC_CLIENT_CMD_FUNC(topic)
 
   if (cmd->argv[1][0] == '*') {
     if (!conn->current_channel) {
-      cmd->client->ops->say(cmd->client, conn, SILC_CLIENT_MESSAGE_INFO, 
+      cmd->client->internal->ops->say(cmd->client, conn, SILC_CLIENT_MESSAGE_INFO, 
                            "You are not on any channel");
       COMMAND_ERROR;
       goto out;
@@ -533,7 +504,7 @@ SILC_CLIENT_CMD_FUNC(topic)
   }
 
   if (!conn->current_channel) {
-    cmd->client->ops->say(cmd->client, conn, SILC_CLIENT_MESSAGE_INFO, 
+    cmd->client->internal->ops->say(cmd->client, conn, SILC_CLIENT_MESSAGE_INFO, 
                          "You are not on that channel");
     COMMAND_ERROR;
     goto out;
@@ -541,7 +512,7 @@ SILC_CLIENT_CMD_FUNC(topic)
 
   /* 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, 
+    cmd->client->internal->ops->say(cmd->client, conn, SILC_CLIENT_MESSAGE_INFO, 
                          "You are not on that channel");
     COMMAND_ERROR;
     goto out;
@@ -595,7 +566,7 @@ SILC_CLIENT_CMD_FUNC(invite)
   }
 
   if (cmd->argc < 2) {
-    cmd->client->ops->say(cmd->client, conn, SILC_CLIENT_MESSAGE_INFO,
+    cmd->client->internal->ops->say(cmd->client, conn, SILC_CLIENT_MESSAGE_INFO,
                   "Usage: /INVITE <channel> [<nickname>[@server>]"
                   "[+|-[<nickname>[@<server>[!<username>[@hostname>]]]]]");
     COMMAND_ERROR;
@@ -604,7 +575,7 @@ SILC_CLIENT_CMD_FUNC(invite)
 
   if (cmd->argv[1][0] == '*') {
     if (!conn->current_channel) {
-      cmd->client->ops->say(cmd->client, conn, SILC_CLIENT_MESSAGE_INFO, 
+      cmd->client->internal->ops->say(cmd->client, conn, SILC_CLIENT_MESSAGE_INFO, 
                            "You are not on any channel");
       COMMAND_ERROR;
       goto out;
@@ -616,7 +587,7 @@ SILC_CLIENT_CMD_FUNC(invite)
 
     channel = silc_client_get_channel(cmd->client, conn, name);
     if (!channel) {
-      cmd->client->ops->say(cmd->client, conn, SILC_CLIENT_MESSAGE_INFO, 
+      cmd->client->internal->ops->say(cmd->client, conn, SILC_CLIENT_MESSAGE_INFO, 
                            "You are on that channel");
       COMMAND_ERROR;
       goto out;
@@ -626,8 +597,8 @@ SILC_CLIENT_CMD_FUNC(invite)
   /* Parse the typed nickname. */
   if (cmd->argc == 3) {
     if (cmd->argv[2][0] != '+' && cmd->argv[2][0] != '-') {
-      if (client->params->nickname_parse)
-       client->params->nickname_parse(cmd->argv[2], &nickname);
+      if (client->internal->params->nickname_parse)
+       client->internal->params->nickname_parse(cmd->argv[2], &nickname);
       else
        nickname = strdup(cmd->argv[2]);
 
@@ -703,7 +674,7 @@ SILC_TASK_CALLBACK(silc_client_command_quit_cb)
   QuitInternal q = (QuitInternal)context;
 
   /* Close connection */
-  q->client->ops->disconnect(q->client, q->conn);
+  q->client->internal->ops->disconnect(q->client, q->conn);
   silc_client_close_connection(q->client, NULL, q->conn->sock->user_data);
 
   silc_free(q);
@@ -765,8 +736,8 @@ SILC_TASK_CALLBACK(silc_client_command_kill_remove_later)
   char *nickname = NULL;
   
   /* Parse the typed nickname. */
-  if (client->params->nickname_parse)
-    client->params->nickname_parse(cmd->argv[1], &nickname);
+  if (client->internal->params->nickname_parse)
+    client->internal->params->nickname_parse(cmd->argv[1], &nickname);
   else
     nickname = strdup(cmd->argv[1]);
 
@@ -823,15 +794,15 @@ SILC_CLIENT_CMD_FUNC(kill)
   }
 
   if (cmd->argc < 2) {
-    cmd->client->ops->say(cmd->client, conn, SILC_CLIENT_MESSAGE_INFO, 
+    cmd->client->internal->ops->say(cmd->client, conn, SILC_CLIENT_MESSAGE_INFO, 
                          "Usage: /KILL <nickname> [<comment>]");
     COMMAND_ERROR;
     goto out;
   }
 
   /* Parse the typed nickname. */
-  if (client->params->nickname_parse)
-    client->params->nickname_parse(cmd->argv[1], &nickname);
+  if (client->internal->params->nickname_parse)
+    client->internal->params->nickname_parse(cmd->argv[1], &nickname);
   else
     nickname = strdup(cmd->argv[1]);
 
@@ -1088,7 +1059,7 @@ SILC_CLIENT_CMD_FUNC(motd)
   }
 
   if (cmd->argc < 1 || cmd->argc > 2) {
-    cmd->client->ops->say(cmd->client, conn, SILC_CLIENT_MESSAGE_INFO,
+    cmd->client->internal->ops->say(cmd->client, conn, SILC_CLIENT_MESSAGE_INFO,
                          "Usage: /MOTD [<server>]");
     COMMAND_ERROR;
     goto out;
@@ -1133,7 +1104,7 @@ SILC_CLIENT_CMD_FUNC(umode)
   }
 
   if (cmd->argc < 2) {
-    cmd->client->ops->say(cmd->client, conn, SILC_CLIENT_MESSAGE_INFO, 
+    cmd->client->internal->ops->say(cmd->client, conn, SILC_CLIENT_MESSAGE_INFO, 
                  "Usage: /UMODE +|-<modes>");
     COMMAND_ERROR;
     goto out;
@@ -1228,7 +1199,7 @@ SILC_CLIENT_CMD_FUNC(cmode)
   }
 
   if (cmd->argc < 3) {
-    cmd->client->ops->say(cmd->client, conn, SILC_CLIENT_MESSAGE_INFO, 
+    cmd->client->internal->ops->say(cmd->client, conn, SILC_CLIENT_MESSAGE_INFO, 
                  "Usage: /CMODE <channel> +|-<modes> [{ <arguments>}]");
     COMMAND_ERROR;
     goto out;
@@ -1236,7 +1207,7 @@ SILC_CLIENT_CMD_FUNC(cmode)
 
   if (cmd->argv[1][0] == '*') {
     if (!conn->current_channel) {
-      cmd->client->ops->say(cmd->client, conn, SILC_CLIENT_MESSAGE_INFO, 
+      cmd->client->internal->ops->say(cmd->client, conn, SILC_CLIENT_MESSAGE_INFO, 
                            "You are not on any channel");
       COMMAND_ERROR;
       goto out;
@@ -1248,7 +1219,7 @@ SILC_CLIENT_CMD_FUNC(cmode)
 
     channel = silc_client_get_channel(cmd->client, conn, name);
     if (!channel) {
-      cmd->client->ops->say(cmd->client, conn, SILC_CLIENT_MESSAGE_INFO, 
+      cmd->client->internal->ops->say(cmd->client, conn, SILC_CLIENT_MESSAGE_INFO, 
                            "You are on that channel");
       COMMAND_ERROR;
       goto out;
@@ -1307,7 +1278,7 @@ SILC_CLIENT_CMD_FUNC(cmode)
        mode |= SILC_CHANNEL_MODE_ULIMIT;
        type = 3;
        if (cmd->argc < 4) {
-         cmd->client->ops->say(cmd->client, conn, SILC_CLIENT_MESSAGE_INFO, 
+         cmd->client->internal->ops->say(cmd->client, conn, SILC_CLIENT_MESSAGE_INFO, 
               "Usage: /CMODE <channel> +|-<modes> [{ <arguments>}]");
          COMMAND_ERROR;
          goto out;
@@ -1325,7 +1296,7 @@ SILC_CLIENT_CMD_FUNC(cmode)
        mode |= SILC_CHANNEL_MODE_PASSPHRASE;
        type = 4;
        if (cmd->argc < 4) {
-         cmd->client->ops->say(cmd->client, conn, SILC_CLIENT_MESSAGE_INFO, 
+         cmd->client->internal->ops->say(cmd->client, conn, SILC_CLIENT_MESSAGE_INFO, 
               "Usage: /CMODE <channel> +|-<modes> [{ <arguments>}]");
          COMMAND_ERROR;
          goto out;
@@ -1341,7 +1312,7 @@ SILC_CLIENT_CMD_FUNC(cmode)
        mode |= SILC_CHANNEL_MODE_CIPHER;
        type = 5;
        if (cmd->argc < 4) {
-         cmd->client->ops->say(cmd->client, conn, SILC_CLIENT_MESSAGE_INFO, 
+         cmd->client->internal->ops->say(cmd->client, conn, SILC_CLIENT_MESSAGE_INFO, 
               "Usage: /CMODE <channel> +|-<modes> [{ <arguments>}]");
          COMMAND_ERROR;
          goto out;
@@ -1357,7 +1328,7 @@ SILC_CLIENT_CMD_FUNC(cmode)
        mode |= SILC_CHANNEL_MODE_HMAC;
        type = 6;
        if (cmd->argc < 4) {
-         cmd->client->ops->say(cmd->client, conn, SILC_CLIENT_MESSAGE_INFO, 
+         cmd->client->internal->ops->say(cmd->client, conn, SILC_CLIENT_MESSAGE_INFO, 
               "Usage: /CMODE <channel> +|-<modes> [{ <arguments>}]");
          COMMAND_ERROR;
          goto out;
@@ -1374,7 +1345,7 @@ SILC_CLIENT_CMD_FUNC(cmode)
        type = 7;
 
        if (cmd->argc < 4) {
-         cmd->client->ops->say(cmd->client, conn, SILC_CLIENT_MESSAGE_INFO, 
+         cmd->client->internal->ops->say(cmd->client, conn, SILC_CLIENT_MESSAGE_INFO, 
               "Usage: /CMODE <channel> +|-<modes> [{ <arguments>}]");
          COMMAND_ERROR;
          goto out;
@@ -1459,7 +1430,7 @@ SILC_CLIENT_CMD_FUNC(cumode)
   }
 
   if (cmd->argc < 4) {
-    cmd->client->ops->say(cmd->client, conn, SILC_CLIENT_MESSAGE_INFO, 
+    cmd->client->internal->ops->say(cmd->client, conn, SILC_CLIENT_MESSAGE_INFO, 
                  "Usage: /CUMODE <channel> +|-<modes> <nickname>[@<server>]");
     COMMAND_ERROR;
     goto out;
@@ -1467,7 +1438,7 @@ SILC_CLIENT_CMD_FUNC(cumode)
 
   if (cmd->argv[1][0] == '*') {
     if (!conn->current_channel) {
-      cmd->client->ops->say(cmd->client, conn, SILC_CLIENT_MESSAGE_INFO, 
+      cmd->client->internal->ops->say(cmd->client, conn, SILC_CLIENT_MESSAGE_INFO, 
                            "You are not on any channel");
       COMMAND_ERROR;
       goto out;
@@ -1479,7 +1450,7 @@ SILC_CLIENT_CMD_FUNC(cumode)
 
     channel = silc_client_get_channel(cmd->client, conn, name);
     if (!channel) {
-      cmd->client->ops->say(cmd->client, conn, SILC_CLIENT_MESSAGE_INFO, 
+      cmd->client->internal->ops->say(cmd->client, conn, SILC_CLIENT_MESSAGE_INFO, 
                            "You are on that channel");
       COMMAND_ERROR;
       goto out;
@@ -1487,8 +1458,8 @@ SILC_CLIENT_CMD_FUNC(cumode)
   }
 
   /* Parse the typed nickname. */
-  if (client->params->nickname_parse)
-    client->params->nickname_parse(cmd->argv[3], &nickname);
+  if (client->internal->params->nickname_parse)
+    client->internal->params->nickname_parse(cmd->argv[3], &nickname);
   else
     nickname = strdup(cmd->argv[3]);
 
@@ -1625,7 +1596,7 @@ SILC_CLIENT_CMD_FUNC(kick)
   }
 
   if (cmd->argc < 3) {
-    cmd->client->ops->say(cmd->client, conn, SILC_CLIENT_MESSAGE_INFO, 
+    cmd->client->internal->ops->say(cmd->client, conn, SILC_CLIENT_MESSAGE_INFO, 
                          "Usage: /KICK <channel> <nickname> [<comment>]");
     COMMAND_ERROR;
     goto out;
@@ -1633,7 +1604,7 @@ SILC_CLIENT_CMD_FUNC(kick)
 
   if (cmd->argv[1][0] == '*') {
     if (!conn->current_channel) {
-      cmd->client->ops->say(cmd->client, conn, SILC_CLIENT_MESSAGE_INFO, 
+      cmd->client->internal->ops->say(cmd->client, conn, SILC_CLIENT_MESSAGE_INFO, 
                            "You are not on any channel");
       COMMAND_ERROR;
       goto out;
@@ -1644,7 +1615,7 @@ SILC_CLIENT_CMD_FUNC(kick)
   }
 
   if (!conn->current_channel) {
-    cmd->client->ops->say(cmd->client, conn, SILC_CLIENT_MESSAGE_INFO, 
+    cmd->client->internal->ops->say(cmd->client, conn, SILC_CLIENT_MESSAGE_INFO, 
                          "You are not on that channel");
     COMMAND_ERROR;
     goto out;
@@ -1652,7 +1623,7 @@ SILC_CLIENT_CMD_FUNC(kick)
 
   /* 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, 
+    cmd->client->internal->ops->say(cmd->client, conn, SILC_CLIENT_MESSAGE_INFO, 
                          "You are not on that channel");
     COMMAND_ERROR;
     goto out;
@@ -1661,8 +1632,8 @@ SILC_CLIENT_CMD_FUNC(kick)
   channel = (SilcChannelEntry)id_cache->context;
 
   /* Parse the typed nickname. */
-  if (client->params->nickname_parse)
-    client->params->nickname_parse(cmd->argv[2], &nickname);
+  if (client->internal->params->nickname_parse)
+    client->internal->params->nickname_parse(cmd->argv[2], &nickname);
   else
     nickname = strdup(cmd->argv[2]);
 
@@ -1670,7 +1641,7 @@ SILC_CLIENT_CMD_FUNC(kick)
   target = silc_idlist_get_client(cmd->client, conn, nickname, 
                                  cmd->argv[2], FALSE);
   if (!target) {
-    cmd->client->ops->say(cmd->client, conn, SILC_CLIENT_MESSAGE_INFO, 
+    cmd->client->internal->ops->say(cmd->client, conn, SILC_CLIENT_MESSAGE_INFO, 
                          "No such client: %s",
                          cmd->argv[2]);
     COMMAND_ERROR;
@@ -1752,7 +1723,7 @@ SILC_CLIENT_CMD_FUNC(oper)
   }
 
   if (cmd->argc < 2) {
-    cmd->client->ops->say(cmd->client, conn, SILC_CLIENT_MESSAGE_INFO, 
+    cmd->client->internal->ops->say(cmd->client, conn, SILC_CLIENT_MESSAGE_INFO, 
                          "Usage: /OPER <username> [-pubkey]");
     COMMAND_ERROR;
     goto out;
@@ -1760,7 +1731,7 @@ SILC_CLIENT_CMD_FUNC(oper)
 
   if (cmd->argc < 3) {
     /* Get passphrase */
-    cmd->client->ops->ask_passphrase(cmd->client, conn,
+    cmd->client->internal->ops->ask_passphrase(cmd->client, conn,
                                     silc_client_command_oper_send,
                                     context);
     return;
@@ -1820,7 +1791,7 @@ SILC_CLIENT_CMD_FUNC(silcoper)
   }
 
   if (cmd->argc < 2) {
-    cmd->client->ops->say(cmd->client, conn, SILC_CLIENT_MESSAGE_INFO, 
+    cmd->client->internal->ops->say(cmd->client, conn, SILC_CLIENT_MESSAGE_INFO, 
                          "Usage: /SILCOPER <username> [-pubkey]");
     COMMAND_ERROR;
     goto out;
@@ -1828,7 +1799,7 @@ SILC_CLIENT_CMD_FUNC(silcoper)
 
   if (cmd->argc < 3) {
     /* Get passphrase */
-    cmd->client->ops->ask_passphrase(cmd->client, conn,
+    cmd->client->internal->ops->ask_passphrase(cmd->client, conn,
                                     silc_client_command_silcoper_send,
                                     context);
     return;
@@ -1857,7 +1828,7 @@ SILC_CLIENT_CMD_FUNC(connect)
   }
 
   if (cmd->argc < 2) {
-    cmd->client->ops->say(cmd->client, conn, SILC_CLIENT_MESSAGE_INFO, 
+    cmd->client->internal->ops->say(cmd->client, conn, SILC_CLIENT_MESSAGE_INFO, 
                          "Usage: /CONNECT <server> [<port>]");
     COMMAND_ERROR;
     goto out;
@@ -1906,7 +1877,7 @@ SILC_CLIENT_CMD_FUNC(ban)
   }
 
   if (cmd->argc < 2) {
-    cmd->client->ops->say(cmd->client, conn, SILC_CLIENT_MESSAGE_INFO, 
+    cmd->client->internal->ops->say(cmd->client, conn, SILC_CLIENT_MESSAGE_INFO, 
                   "Usage: /BAN <channel> "
                   "[+|-[<nickname>[@<server>[!<username>[@hostname>]]]]]");
     COMMAND_ERROR;
@@ -1915,7 +1886,7 @@ SILC_CLIENT_CMD_FUNC(ban)
 
   if (cmd->argv[1][0] == '*') {
     if (!conn->current_channel) {
-      cmd->client->ops->say(cmd->client, conn, SILC_CLIENT_MESSAGE_INFO, 
+      cmd->client->internal->ops->say(cmd->client, conn, SILC_CLIENT_MESSAGE_INFO, 
                            "You are not on any channel");
       COMMAND_ERROR;
       goto out;
@@ -1927,7 +1898,7 @@ SILC_CLIENT_CMD_FUNC(ban)
 
     channel = silc_client_get_channel(cmd->client, conn, name);
     if (!channel) {
-      cmd->client->ops->say(cmd->client, conn, SILC_CLIENT_MESSAGE_INFO, 
+      cmd->client->internal->ops->say(cmd->client, conn, SILC_CLIENT_MESSAGE_INFO, 
                            "You are on that channel");
       COMMAND_ERROR;
       goto out;
@@ -1984,7 +1955,7 @@ SILC_CLIENT_CMD_FUNC(close)
   }
 
   if (cmd->argc < 2) {
-    cmd->client->ops->say(cmd->client, conn, SILC_CLIENT_MESSAGE_INFO, 
+    cmd->client->internal->ops->say(cmd->client, conn, SILC_CLIENT_MESSAGE_INFO, 
                          "Usage: /CLOSE <server> [<port>]");
     COMMAND_ERROR;
     goto out;
@@ -2028,7 +1999,7 @@ SILC_CLIENT_CMD_FUNC(shutdown)
   }
 
   /* Send the command */
-  silc_client_send_command(cmd->client, cmd->conn, 
+  silc_client_command_send(cmd->client, cmd->conn, 
                           SILC_COMMAND_SHUTDOWN, 0, 0);
 
   /* Notify application */
@@ -2056,7 +2027,7 @@ SILC_CLIENT_CMD_FUNC(leave)
   }
 
   if (cmd->argc != 2) {
-    cmd->client->ops->say(cmd->client, conn, SILC_CLIENT_MESSAGE_INFO, 
+    cmd->client->internal->ops->say(cmd->client, conn, SILC_CLIENT_MESSAGE_INFO, 
                          "Usage: /LEAVE <channel>");
     COMMAND_ERROR;
     goto out;
@@ -2064,7 +2035,7 @@ SILC_CLIENT_CMD_FUNC(leave)
 
   if (cmd->argv[1][0] == '*') {
     if (!conn->current_channel) {
-      cmd->client->ops->say(cmd->client, conn, SILC_CLIENT_MESSAGE_INFO, 
+      cmd->client->internal->ops->say(cmd->client, conn, SILC_CLIENT_MESSAGE_INFO, 
                            "You are not on any channel");
       COMMAND_ERROR;
       goto out;
@@ -2076,7 +2047,7 @@ 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, 
+    cmd->client->internal->ops->say(cmd->client, conn, SILC_CLIENT_MESSAGE_INFO, 
                          "You are not on that channel");
     COMMAND_ERROR;
     goto out;
@@ -2123,7 +2094,7 @@ SILC_CLIENT_CMD_FUNC(users)
   }
 
   if (cmd->argc != 2) {
-    cmd->client->ops->say(cmd->client, conn, SILC_CLIENT_MESSAGE_INFO, 
+    cmd->client->internal->ops->say(cmd->client, conn, SILC_CLIENT_MESSAGE_INFO, 
                          "Usage: /USERS <channel>");
     COMMAND_ERROR;
     goto out;
@@ -2131,7 +2102,7 @@ SILC_CLIENT_CMD_FUNC(users)
 
   if (cmd->argv[1][0] == '*') {
     if (!conn->current_channel) {
-      cmd->client->ops->say(cmd->client, conn, SILC_CLIENT_MESSAGE_INFO, 
+      cmd->client->internal->ops->say(cmd->client, conn, SILC_CLIENT_MESSAGE_INFO, 
                            "You are not on any channel");
       COMMAND_ERROR;
       goto out;
@@ -2176,7 +2147,7 @@ SILC_CLIENT_CMD_FUNC(getkey)
   }
 
   if (cmd->argc < 2) {
-    client->ops->say(client, conn, SILC_CLIENT_MESSAGE_INFO, 
+    client->internal->ops->say(client, conn, SILC_CLIENT_MESSAGE_INFO, 
                     "Usage: /GETKEY <nickname or server name>");
     COMMAND_ERROR;
     goto out;
@@ -2191,7 +2162,7 @@ SILC_CLIENT_CMD_FUNC(getkey)
     
     if (status == SILC_STATUS_ERR_NO_SUCH_NICK ||
        status == SILC_STATUS_ERR_NO_SUCH_SERVER) {
-      cmd->client->ops->say(cmd->client, conn, SILC_CLIENT_MESSAGE_ERROR,
+      cmd->client->internal->ops->say(cmd->client, conn, SILC_CLIENT_MESSAGE_ERROR,
                            "%s", 
                            silc_client_command_status_message(status));
       COMMAND_ERROR;
@@ -2200,8 +2171,8 @@ SILC_CLIENT_CMD_FUNC(getkey)
   }
 
   /* Parse the typed nickname. */
-  if (client->params->nickname_parse)
-    client->params->nickname_parse(cmd->argv[1], &nickname);
+  if (client->internal->params->nickname_parse)
+    client->internal->params->nickname_parse(cmd->argv[1], &nickname);
   else
     nickname = strdup(cmd->argv[1]);
 
@@ -2231,8 +2202,11 @@ SILC_CLIENT_CMD_FUNC(getkey)
                                  silc_client_command_dup(cmd));
 
       /* This sends the IDENTIFY command to resolve the server. */
-      silc_client_send_command(client, conn, SILC_COMMAND_IDENTIFY,
-                              ++conn->cmd_ident, 1, 
+      silc_client_command_register(client, SILC_COMMAND_IDENTIFY, NULL, NULL,
+                                  silc_client_command_reply_identify_i, 0,
+                                  ++conn->cmd_ident);
+      silc_client_command_send(client, conn, SILC_COMMAND_IDENTIFY,
+                              conn->cmd_ident, 1, 
                               2, cmd->argv[1], cmd->argv_lens[1]);
       silc_client_command_pending(conn, SILC_COMMAND_IDENTIFY, 
                                  conn->cmd_ident, NULL,
@@ -2263,3 +2237,135 @@ SILC_CLIENT_CMD_FUNC(getkey)
   silc_free(nickname);
   silc_client_command_free(cmd);
 }
+
+/* Register a new command indicated by the `command' to the SILC client.
+   The `name' is optional command name.  If provided the command may be
+   searched using the silc_client_command_find by that name.  The
+   `command_function' is the function to be called when the command is
+   executed, and the `command_reply_function' is the function to be
+   called after the server has sent reply back to the command. 
+
+   The `ident' is optional identifier for the command.  If non-zero
+   the `command_reply_function' for the command type `command' will be
+   called only if the command reply sent by server includes the 
+   command identifier `ident'. Application usually does not need it
+   and set it to zero value. */
+
+bool silc_client_command_register(SilcClient client,
+                                 SilcCommand command,
+                                 const char *name,
+                                 SilcCommandCb command_function,
+                                 SilcCommandCb command_reply_function,
+                                 uint8 max_args,
+                                 uint16 ident)
+{
+  SilcClientCommand cmd;
+
+  cmd = silc_calloc(1, sizeof(*cmd));
+  cmd->cmd = command;
+  cmd->command = command_function;
+  cmd->reply = command_reply_function;
+  cmd->name = name ? strdup(name) : NULL;
+  cmd->max_args = max_args;
+  cmd->ident = ident;
+
+  silc_list_add(client->internal->commands, cmd);
+
+  return TRUE;
+}
+
+/* Unregister a command indicated by the `command' with command function
+   `command_function' and command reply function `command_reply_function'.
+   Returns TRUE if the command was found and unregistered. */
+
+bool silc_client_command_unregister(SilcClient client,
+                                   SilcCommand command,
+                                   SilcCommandCb command_function,
+                                   SilcCommandCb command_reply_function,
+                                   uint16 ident)
+{
+  SilcClientCommand cmd;
+
+  silc_list_start(client->internal->commands);
+  while ((cmd = silc_list_get(client->internal->commands)) != SILC_LIST_END) {
+    if (cmd->cmd == command && cmd->command == command_function &&
+       cmd->reply == command_reply_function && cmd->ident == ident) {
+      silc_list_del(client->internal->commands, cmd);
+      silc_free(cmd->name);
+      silc_free(cmd);
+      return TRUE;
+    }
+  }
+
+  return FALSE;
+}
+
+/* Register all default commands provided by the client library for the
+   application. */
+
+void silc_client_commands_register(SilcClient client)
+{
+  silc_list_init(client->internal->commands, struct SilcClientCommandStruct, 
+                next);
+
+  SILC_CLIENT_CMD(whois, WHOIS, "WHOIS", 3);
+  SILC_CLIENT_CMD(whowas, WHOWAS, "WHOWAS", 3);
+  SILC_CLIENT_CMD(identify, IDENTIFY, "IDENTIFY", 3);
+  SILC_CLIENT_CMD(nick, NICK, "NICK", 2);
+  SILC_CLIENT_CMD(list, LIST, "LIST", 2);
+  SILC_CLIENT_CMD(topic, TOPIC, "TOPIC", 3);
+  SILC_CLIENT_CMD(invite, INVITE, "INVITE", 3);
+  SILC_CLIENT_CMD(quit, QUIT, "QUIT", 2);
+  SILC_CLIENT_CMD(kill, KILL, "KILL", 3);
+  SILC_CLIENT_CMD(info, INFO, "INFO", 2);
+  SILC_CLIENT_CMD(connect, CONNECT, "CONNECT", 3);
+  SILC_CLIENT_CMD(ping, PING, "PING", 2);
+  SILC_CLIENT_CMD(oper, OPER, "OPER", 2);
+  SILC_CLIENT_CMD(join, JOIN, "JOIN", 9);
+  SILC_CLIENT_CMD(motd, MOTD, "MOTD", 2);
+  SILC_CLIENT_CMD(umode, UMODE, "UMODE", 2);
+  SILC_CLIENT_CMD(cmode, CMODE, "CMODE", 4);
+  SILC_CLIENT_CMD(cumode, CUMODE, "CUMODE", 5);
+  SILC_CLIENT_CMD(kick, KICK, "KICK", 4);
+  SILC_CLIENT_CMD(ban, BAN, "BAN", 3);
+  SILC_CLIENT_CMD(close, CLOSE, "CLOSE", 3);
+  SILC_CLIENT_CMD(shutdown, SHUTDOWN, "SHUTDOWN", 1);
+  SILC_CLIENT_CMD(silcoper, SILCOPER, "SILCOPER", 3);
+  SILC_CLIENT_CMD(leave, LEAVE, "LEAVE", 2);
+  SILC_CLIENT_CMD(users, USERS, "USERS", 2);
+  SILC_CLIENT_CMD(getkey, GETKEY, "GETKEY", 2);
+}
+
+/* Unregister all commands. */
+
+void silc_client_commands_unregister(SilcClient client)
+{
+  SILC_CLIENT_CMDU(whois, WHOIS, "WHOIS");
+  SILC_CLIENT_CMDU(whowas, WHOWAS, "WHOWAS");
+  SILC_CLIENT_CMDU(identify, IDENTIFY, "IDENTIFY");
+  SILC_CLIENT_CMDU(nick, NICK, "NICK");
+  SILC_CLIENT_CMDU(list, LIST, "LIST");
+  SILC_CLIENT_CMDU(topic, TOPIC, "TOPIC");
+  SILC_CLIENT_CMDU(invite, INVITE, "INVITE");
+  SILC_CLIENT_CMDU(quit, QUIT, "QUIT");
+  SILC_CLIENT_CMDU(kill, KILL, "KILL");
+  SILC_CLIENT_CMDU(info, INFO, "INFO");
+  SILC_CLIENT_CMDU(connect, CONNECT, "CONNECT");
+  SILC_CLIENT_CMDU(ping, PING, "PING");
+  SILC_CLIENT_CMDU(oper, OPER, "OPER");
+  SILC_CLIENT_CMDU(join, JOIN, "JOIN");
+  SILC_CLIENT_CMDU(motd, MOTD, "MOTD");
+  SILC_CLIENT_CMDU(umode, UMODE, "UMODE");
+  SILC_CLIENT_CMDU(cmode, CMODE, "CMODE");
+  SILC_CLIENT_CMDU(cumode, CUMODE, "CUMODE");
+  SILC_CLIENT_CMDU(kick, KICK, "KICK");
+  SILC_CLIENT_CMDU(ban, BAN, "BAN");
+  SILC_CLIENT_CMDU(close, CLOSE, "CLOSE");
+  SILC_CLIENT_CMDU(shutdown, SHUTDOWN, "SHUTDOWN");
+  SILC_CLIENT_CMDU(silcoper, SILCOPER, "SILCOPER");
+  SILC_CLIENT_CMDU(leave, LEAVE, "LEAVE");
+  SILC_CLIENT_CMDU(users, USERS, "USERS");
+  SILC_CLIENT_CMDU(getkey, GETKEY, "GETKEY");
+
+  silc_list_uninit(client->internal->commands);
+}
index 79b4213028c4fdc904313209f082405ef5d28d4b..6b8bd6266eefacb18e5a1ca54688dcee5c57b431 100644 (file)
@@ -1,16 +1,15 @@
 /*
 
-  command.h
+  command.h 
 
-  Author: Pekka Riikonen <priikone@poseidon.pspt.fi>
+  Author: Pekka Riikonen <priikone@silcnet.org>
 
   Copyright (C) 1997 - 2001 Pekka Riikonen
 
   This program is free software; you can redistribute it and/or modify
   it under the terms of the GNU General Public License as published by
-  the Free Software Foundation; either version 2 of the License, or
-  (at your option) any later version.
-  
+  the Free Software Foundation; version 2 of the License.
+
   This program is distributed in the hope that it will be useful,
   but WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 #ifndef COMMAND_H
 #define COMMAND_H
 
-/* 
-   Structure holding one command and pointer to its function. 
-
-   SilcCommandCb cb
-
-       Callback function called when this command is executed.
-
-   SilcCommand cmd
-
-       The actual command. These are defined in silccore/silccommand.h
-
-   char *name
+/* Forward declarations */
+typedef struct SilcClientCommandStruct *SilcClientCommand;
+typedef struct SilcClientCommandContextStruct *SilcClientCommandContext;
 
-       Logical name of the command. This is the visible command name
-       that user uses when calling command. Eg. NICK.
+#include "silcapi.h"
+#include "command_reply.h"
 
-   SilcCommandFlag flags
+/* Structure holding one command and pointer to its function. This
+   structure is allocate into the commands list, and is returned
+   for example by silc_client_command_find function.
 
-       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 (actually we preserve them but
-       ignore them :)).
+   To call a command: command->command(cmd, NULL);
+   To call a command reply: command->reply(cmd, NULL);
 
 */
-typedef struct {
-  SilcCommandCb cb;
-  SilcCommand cmd;
-  char *name;
-  SilcCommandFlag flags;
-  uint32 max_args;
-} SilcClientCommand;
-
-/* All client commands */
-extern SilcClientCommand silc_command_list[];
+struct SilcClientCommandStruct {
+  SilcCommand cmd;                /* Command type */
+  SilcCommandCb command;          /* Command function */
+  SilcCommandCb reply;            /* Command reply callback */
+  char *name;                     /* Name of the command (optional) */
+  uint8 max_args;                 /* Maximum arguments (optional)  */
+  uint16 ident;                           /* Identifier for command (optional)  */
+  struct SilcClientCommandStruct *next;
+};
 
 /* Context sent as argument to all commands. This is used by the library
    and application should use this as well. However, application may
    choose to use some own context for its own local command. All library
    commands, however, must use this context. */
-typedef struct {
+struct SilcClientCommandContextStruct {
   SilcClient client;
   SilcClientConnection conn;
-  SilcClientCommand *command;
+  SilcClientCommand command;
   uint32 argc;
   unsigned char **argv;
   uint32 *argv_lens;
   uint32 *argv_types;
   int pending;                 /* Command is being re-processed when TRUE */
   int users;                   /* Reference counter */
-} *SilcClientCommandContext;
-
-#include "silcapi.h"
+};
 
 /* Structure holding pending commands. If command is pending it will be
    executed after command reply has been executed. */
@@ -89,16 +75,21 @@ typedef struct SilcClientCommandPendingStruct {
 /* List of pending commands */
 extern SilcClientCommandPending *silc_command_pending;
 
-#include "command_reply.h"
 
 /* Macros */
 
-/* Macro used for command declaration in command list structure */
-#define SILC_CLIENT_CMD(func, cmd, name, flags, args) \
-{ silc_client_command_##func, SILC_COMMAND_##cmd, name, flags, args }
+/* Macro used for command registering and unregistering */
+#define SILC_CLIENT_CMD(func, cmd, name, args)                         \
+silc_client_command_register(client, SILC_COMMAND_##cmd, name,                 \
+                            silc_client_command_##func,                \
+                            silc_client_command_reply_##func, args, 0)
+#define SILC_CLIENT_CMDU(func, cmd, name)                              \
+silc_client_command_unregister(client, SILC_COMMAND_##cmd,             \
+                              silc_client_command_##func,              \
+                              silc_client_command_reply_##func, 0)
 
 /* Macro used to declare command functions */
-#define SILC_CLIENT_CMD_FUNC(func) \
+#define SILC_CLIENT_CMD_FUNC(func)                             \
 void silc_client_command_##func(void *context, void *context2)
 
 /* Executed pending command callback */
@@ -117,7 +108,20 @@ do {                                                                       \
     (*ctx->destructor)(ctx->context);                                  \
 } while(0)
 
-/* Prototypes (some prototypes are in the silcapi.h file) */
+bool silc_client_command_register(SilcClient client,
+                                 SilcCommand command,
+                                 const char *name,
+                                 SilcCommandCb command_function,
+                                 SilcCommandCb command_reply_function,
+                                 uint8 max_args,
+                                 uint16 ident);
+bool silc_client_command_unregister(SilcClient client,
+                                   SilcCommand command,
+                                   SilcCommandCb command_function,
+                                   SilcCommandCb command_reply_function,
+                                   uint16 ident);
+void silc_client_commands_register(SilcClient client);
+void silc_client_commands_unregister(SilcClient client);
 void silc_client_command_pending_del(SilcClientConnection conn,
                                     SilcCommand reply_cmd,
                                     uint16 ident);
index a9d3ba704ceb4eabd1fb7e8d9dda66259fdcc487..f66c19dd64f1ff5f69c6287eb4cf79f311a23707 100644 (file)
@@ -1,22 +1,22 @@
 /*
 
-  command_reply.c
+  command_reply.c 
 
-  Author: Pekka Riikonen <priikone@poseidon.pspt.fi>
+  Author: Pekka Riikonen <priikone@silcnet.org>
 
   Copyright (C) 1997 - 2001 Pekka Riikonen
 
   This program is free software; you can redistribute it and/or modify
   it under the terms of the GNU General Public License as published by
-  the Free Software Foundation; either version 2 of the License, or
-  (at your option) any later version.
-  
+  the Free Software Foundation; version 2 of the License.
+
   This program is distributed in the hope that it will be useful,
   but WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   GNU General Public License for more details.
 
 */
+/* $Id$ */
 /*
  * Command reply functions are "the otherside" of the command functions.
  * Reply to a command sent by server is handled by these functions.
  * necessary data already in hand without redundant searching.  If ID is
  * received but ID entry does not exist, NULL is sent.
  */
-/* $Id$ */
 
 #include "clientlibincludes.h"
 #include "client_internal.h"
 
-/* Client command reply list. */
-SilcClientCommandReply silc_command_reply_list[] =
-{
-  SILC_CLIENT_CMD_REPLY(whois, WHOIS),
-  SILC_CLIENT_CMD_REPLY(whowas, WHOWAS),
-  SILC_CLIENT_CMD_REPLY(identify, IDENTIFY),
-  SILC_CLIENT_CMD_REPLY(nick, NICK),
-  SILC_CLIENT_CMD_REPLY(list, LIST),
-  SILC_CLIENT_CMD_REPLY(topic, TOPIC),
-  SILC_CLIENT_CMD_REPLY(invite, INVITE),
-  SILC_CLIENT_CMD_REPLY(kill, KILL),
-  SILC_CLIENT_CMD_REPLY(info, INFO),
-  SILC_CLIENT_CMD_REPLY(connect, CONNECT),
-  SILC_CLIENT_CMD_REPLY(ping, PING),
-  SILC_CLIENT_CMD_REPLY(oper, OPER),
-  SILC_CLIENT_CMD_REPLY(join, JOIN),
-  SILC_CLIENT_CMD_REPLY(motd, MOTD),
-  SILC_CLIENT_CMD_REPLY(umode, UMODE),
-  SILC_CLIENT_CMD_REPLY(cmode, CMODE),
-  SILC_CLIENT_CMD_REPLY(cumode, CUMODE),
-  SILC_CLIENT_CMD_REPLY(kick, KICK),
-  SILC_CLIENT_CMD_REPLY(ban, BAN),
-  SILC_CLIENT_CMD_REPLY(close, CLOSE),
-  SILC_CLIENT_CMD_REPLY(shutdown, SHUTDOWN),
-  SILC_CLIENT_CMD_REPLY(silcoper, SILCOPER),
-  SILC_CLIENT_CMD_REPLY(leave, LEAVE),
-  SILC_CLIENT_CMD_REPLY(users, USERS),
-  SILC_CLIENT_CMD_REPLY(getkey, GETKEY),
-
-  { NULL, 0 },
-};
-
 const SilcCommandStatusMessage silc_command_status_messages[] = {
 
   { STAT(NO_SUCH_NICK),      "There was no such nickname" },
@@ -113,14 +80,14 @@ const SilcCommandStatusMessage silc_command_status_messages[] = {
 };
 /* Command reply operation that is called at the end of all command replys. 
    Usage: COMMAND_REPLY((ARGS, argument1, argument2, etc...)), */
-#define COMMAND_REPLY(args) cmd->client->ops->command_reply args
-#define ARGS cmd->client, cmd->sock->user_data, \
+#define COMMAND_REPLY(args) cmd->client->internal->ops->command_reply args
+#define ARGS cmd->client, cmd->sock->user_data,                                \
              cmd->payload, TRUE, silc_command_get(cmd->payload), status
 
 /* Error reply to application. Usage: COMMAND_REPLY_ERROR; */
-#define COMMAND_REPLY_ERROR cmd->client->ops->command_reply(cmd->client, \
-  cmd->sock->user_data, cmd->payload, FALSE, \
-  silc_command_get(cmd->payload), status)
+#define COMMAND_REPLY_ERROR cmd->client->internal->ops->               \
+  command_reply(cmd->client, cmd->sock->user_data, cmd->payload,       \
+  FALSE, silc_command_get(cmd->payload), status)
 
 /* All functions that call the COMMAND_CHECK_STATUS or the
    COMMAND_CHECK_STATUS_LIST macros must have out: goto label. */
@@ -155,12 +122,12 @@ void silc_client_command_reply_process(SilcClient client,
                                       SilcPacketContext *packet)
 {
   SilcBuffer buffer = packet->buffer;
-  SilcClientCommandReply *cmd;
+  SilcClientCommand cmd;
   SilcClientCommandReplyContext ctx;
   SilcCommandPayload payload;
   SilcCommand command;
-  uint16 ident;
-
+  SilcCommandCb reply = NULL;
+  
   /* Get command reply payload from packet */
   payload = silc_command_payload_parse(buffer->data, buffer->len);
   if (!payload) {
@@ -177,24 +144,35 @@ void silc_client_command_reply_process(SilcClient client,
   ctx->payload = payload;
   ctx->args = silc_command_get_args(ctx->payload);
   ctx->packet = packet;
-  ident = silc_command_get_ident(ctx->payload);
-      
+  ctx->ident = silc_command_get_ident(ctx->payload);
+
   /* Check for pending commands and mark to be exeucted */
   silc_client_command_pending_check(sock->user_data, ctx, 
-                                   silc_command_get(ctx->payload), ident);
+                                   silc_command_get(ctx->payload), 
+                                   ctx->ident);
 
   /* Execute command reply */
+
   command = silc_command_get(ctx->payload);
-  for (cmd = silc_command_reply_list; cmd->cb; cmd++)
-    if (cmd->cmd == command)
-      break;
 
-  if (cmd == NULL || !cmd->cb) {
-    silc_free(ctx);
-    return;
+  /* Try to find matching the command identifier */
+  silc_list_start(client->internal->commands);
+  while ((cmd = silc_list_get(client->internal->commands)) != SILC_LIST_END) {
+    if (cmd->cmd == command && !cmd->ident)
+      reply = cmd->reply;
+    if (cmd->cmd == command && cmd->ident == ctx->ident) {
+      (*cmd->reply)((void *)ctx, NULL);
+      break;
+    }
   }
 
-  cmd->cb(ctx, NULL);
+  if (cmd == SILC_LIST_END) {
+    if (reply)
+      /* No specific identifier for command reply, call first one found */
+      (*reply)(ctx, NULL);
+    else
+      silc_free(ctx);
+  }
 }
 
 /* Returns status message string */
@@ -226,7 +204,8 @@ void silc_client_command_reply_free(SilcClientCommandReplyContext cmd)
 
 static void 
 silc_client_command_reply_whois_save(SilcClientCommandReplyContext cmd,
-                                    SilcCommandStatus status)
+                                    SilcCommandStatus status,
+                                    bool notify)
 {
   SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
   SilcClientID *client_id;
@@ -246,13 +225,15 @@ silc_client_command_reply_whois_save(SilcClientCommandReplyContext cmd,
 
   id_data = silc_argument_get_arg_type(cmd->args, 2, &len);
   if (!id_data) {
-    COMMAND_REPLY_ERROR;
+    if (notify)
+      COMMAND_REPLY_ERROR;
     return;
   }
   
   client_id = silc_id_payload_parse_id(id_data, len);
   if (!client_id) {
-    COMMAND_REPLY_ERROR;
+    if (notify)
+      COMMAND_REPLY_ERROR;
     return;
   }
   
@@ -260,7 +241,8 @@ silc_client_command_reply_whois_save(SilcClientCommandReplyContext cmd,
   username = silc_argument_get_arg_type(cmd->args, 4, &len);
   realname = silc_argument_get_arg_type(cmd->args, 5, &len);
   if (!nickname || !username || !realname) {
-    COMMAND_REPLY_ERROR;
+    if (notify)
+      COMMAND_REPLY_ERROR;
     return;
   }
 
@@ -309,7 +291,7 @@ silc_client_command_reply_whois_save(SilcClientCommandReplyContext cmd,
     client_entry->status &= ~SILC_CLIENT_STATUS_RESOLVING;
 
   /* Notify application */
-  if (!cmd->callback)
+  if (!cmd->callback && notify)
     COMMAND_REPLY((ARGS, client_entry, nickname, username, realname, 
                   channels, mode, idle, fingerprint));
 
@@ -323,12 +305,13 @@ silc_client_command_reply_whois_save(SilcClientCommandReplyContext cmd,
 SILC_CLIENT_CMD_REPLY_FUNC(whois)
 {
   SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
+  SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
   SilcCommandStatus status;
 
   COMMAND_CHECK_STATUS_LIST;
 
   /* Save WHOIS info */
-  silc_client_command_reply_whois_save(cmd, status);
+  silc_client_command_reply_whois_save(cmd, status, TRUE);
 
   /* Pending callbacks are not executed if this was an list entry */
   if (status != SILC_STATUS_OK &&
@@ -340,6 +323,27 @@ SILC_CLIENT_CMD_REPLY_FUNC(whois)
  out:
   SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_WHOIS);
   SILC_CLIENT_PENDING_DESTRUCTOR(cmd, SILC_COMMAND_WHOIS);
+
+  /* If we received notify for invalid ID we'll remove the ID if we
+     have it cached. */
+  if (status == SILC_STATUS_ERR_NO_SUCH_CLIENT_ID) {
+    SilcClientEntry client_entry;
+    uint32 tmp_len;
+    unsigned char *tmp =
+      silc_argument_get_arg_type(silc_command_get_args(cmd->payload),
+                                2, &tmp_len);
+    if (tmp) {
+      SilcClientID *client_id = silc_id_payload_parse_id(tmp, tmp_len);
+      if (client_id) {
+       client_entry = silc_client_get_client_by_id(cmd->client, conn,
+                                                   client_id);
+       if (client_entry)
+         silc_client_del_client(cmd->client, conn, client_entry);
+       silc_free(client_id);
+      }
+    }
+  }
+
   silc_client_command_reply_free(cmd);
 }
 
@@ -408,7 +412,8 @@ SILC_CLIENT_CMD_REPLY_FUNC(whowas)
 
 static void 
 silc_client_command_reply_identify_save(SilcClientCommandReplyContext cmd,
-                                       SilcCommandStatus status)
+                                       SilcCommandStatus status,
+                                       bool notify)
 {
   SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
   SilcClient client = cmd->client;
@@ -430,12 +435,14 @@ silc_client_command_reply_identify_save(SilcClientCommandReplyContext cmd,
 
   id_data = silc_argument_get_arg_type(cmd->args, 2, &len);
   if (!id_data) {
-    COMMAND_REPLY_ERROR;
+    if (notify)
+      COMMAND_REPLY_ERROR;
     return;
   }
   idp = silc_id_payload_parse(id_data, len);
   if (!idp) {
-    COMMAND_REPLY_ERROR;
+    if (notify)
+      COMMAND_REPLY_ERROR;
     return;
   }
 
@@ -470,7 +477,8 @@ silc_client_command_reply_identify_save(SilcClientCommandReplyContext cmd,
       client_entry->status &= ~SILC_CLIENT_STATUS_RESOLVING;
 
     /* Notify application */
-    COMMAND_REPLY((ARGS, client_entry, name, info));
+    if (notify)
+      COMMAND_REPLY((ARGS, client_entry, name, info));
     break;
 
   case SILC_ID_SERVER:
@@ -499,7 +507,8 @@ silc_client_command_reply_identify_save(SilcClientCommandReplyContext cmd,
     }
 
     /* Notify application */
-    COMMAND_REPLY((ARGS, server_entry, name, info));
+    if (notify)
+      COMMAND_REPLY((ARGS, server_entry, name, info));
     break;
 
   case SILC_ID_CHANNEL:
@@ -521,7 +530,8 @@ silc_client_command_reply_identify_save(SilcClientCommandReplyContext cmd,
     }
 
     /* Notify application */
-    COMMAND_REPLY((ARGS, channel_entry, name, info));
+    if (notify)
+      COMMAND_REPLY((ARGS, channel_entry, name, info));
     break;
   }
 
@@ -538,12 +548,13 @@ silc_client_command_reply_identify_save(SilcClientCommandReplyContext cmd,
 SILC_CLIENT_CMD_REPLY_FUNC(identify)
 {
   SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
+  SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
   SilcCommandStatus status;
 
   COMMAND_CHECK_STATUS_LIST;
 
   /* Save IDENTIFY info */
-  silc_client_command_reply_identify_save(cmd, status);
+  silc_client_command_reply_identify_save(cmd, status, TRUE);
 
   /* Pending callbacks are not executed if this was an list entry */
   if (status != SILC_STATUS_OK &&
@@ -555,6 +566,27 @@ SILC_CLIENT_CMD_REPLY_FUNC(identify)
  out:
   SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_IDENTIFY);
   SILC_CLIENT_PENDING_DESTRUCTOR(cmd, SILC_COMMAND_IDENTIFY);
+
+  /* If we received notify for invalid ID we'll remove the ID if we
+     have it cached. */
+  if (status == SILC_STATUS_ERR_NO_SUCH_CLIENT_ID) {
+    SilcClientEntry client_entry;
+    uint32 tmp_len;
+    unsigned char *tmp =
+      silc_argument_get_arg_type(silc_command_get_args(cmd->payload),
+                                2, &tmp_len);
+    if (tmp) {
+      SilcClientID *client_id = silc_id_payload_parse_id(tmp, tmp_len);
+      if (client_id) {
+       client_entry = silc_client_get_client_by_id(cmd->client, conn,
+                                                   client_id);
+       if (client_entry)
+         silc_client_del_client(cmd->client, conn, client_entry);
+       silc_free(client_id);
+      }
+    }
+  }
+
   silc_client_command_reply_free(cmd);
 }
 
@@ -574,16 +606,18 @@ SILC_CLIENT_CMD_REPLY_FUNC(nick)
 
   SILC_GET16_MSB(status, silc_argument_get_arg_type(cmd->args, 1, NULL));
   if (status != SILC_STATUS_OK) {
-    cmd->client->ops->say(cmd->client, conn, SILC_CLIENT_MESSAGE_ERROR,
-                         "Cannot set nickname: %s", 
-                         silc_client_command_status_message(status));
+    cmd->client->internal->ops->say(
+                            cmd->client, 
+                            conn, SILC_CLIENT_MESSAGE_ERROR,
+                            "Cannot set nickname: %s", 
+                            silc_client_command_status_message(status));
     COMMAND_REPLY_ERROR;
     goto out;
   }
 
   argc = silc_argument_get_arg_num(cmd->args);
   if (argc < 2 || argc > 2) {
-    cmd->client->ops->say(cmd->client, conn, SILC_CLIENT_MESSAGE_ERROR,
+    cmd->client->internal->ops->say(cmd->client, conn, SILC_CLIENT_MESSAGE_ERROR,
                          "Cannot set nickname: bad reply to command");
     COMMAND_REPLY_ERROR;
     goto out;
@@ -660,7 +694,7 @@ SILC_CLIENT_CMD_REPLY_FUNC(topic)
 
   SILC_GET16_MSB(status, silc_argument_get_arg_type(cmd->args, 1, NULL));
   if (status != SILC_STATUS_OK) {
-    cmd->client->ops->say(cmd->client, conn, SILC_CLIENT_MESSAGE_ERROR,
+    cmd->client->internal->ops->say(cmd->client, conn, SILC_CLIENT_MESSAGE_ERROR,
             "%s", silc_client_command_status_message(status));
     COMMAND_REPLY_ERROR;
     goto out;
@@ -721,7 +755,7 @@ SILC_CLIENT_CMD_REPLY_FUNC(invite)
   tmp = silc_argument_get_arg_type(cmd->args, 1, NULL);
   SILC_GET16_MSB(status, tmp);
   if (status != SILC_STATUS_OK) {
-    cmd->client->ops->say(cmd->client, conn, SILC_CLIENT_MESSAGE_ERROR,
+    cmd->client->internal->ops->say(cmd->client, conn, SILC_CLIENT_MESSAGE_ERROR,
             "%s", silc_client_command_status_message(status));
     COMMAND_REPLY_ERROR;
     goto out;
@@ -768,7 +802,7 @@ SILC_CLIENT_CMD_REPLY_FUNC(kill)
 
   SILC_GET16_MSB(status, silc_argument_get_arg_type(cmd->args, 1, NULL));
   if (status != SILC_STATUS_OK) {
-    cmd->client->ops->say(cmd->client, conn, SILC_CLIENT_MESSAGE_ERROR,
+    cmd->client->internal->ops->say(cmd->client, conn, SILC_CLIENT_MESSAGE_ERROR,
             "%s", silc_client_command_status_message(status));
     COMMAND_REPLY_ERROR;
     goto out;
@@ -798,11 +832,16 @@ SILC_CLIENT_CMD_REPLY_FUNC(info)
   char *server_name, *server_info;
   uint32 len;
 
+  SILC_LOG_DEBUG(("Start"));
+
   tmp = silc_argument_get_arg_type(cmd->args, 1, NULL);
   SILC_GET16_MSB(status, tmp);
   if (status != SILC_STATUS_OK) {
-    cmd->client->ops->say(cmd->client, conn, SILC_CLIENT_MESSAGE_ERROR,
-            "%s", silc_client_command_status_message(status));
+    cmd->client->internal->ops->say(
+                         cmd->client, conn, 
+                         SILC_CLIENT_MESSAGE_ERROR,
+                         "%s", 
+                         silc_client_command_status_message(status));
     COMMAND_REPLY_ERROR;
     goto out;
   }
@@ -839,9 +878,6 @@ SILC_CLIENT_CMD_REPLY_FUNC(info)
     /* Add it to the cache */
     silc_idcache_add(conn->server_cache, server->server_name,
                     server->server_id, (void *)server, 0, NULL);
-
-    if (SILC_ID_SERVER_COMPARE(server_id, conn->remote_id))
-      goto out;
   } else {
     server = (SilcServerEntry)id_cache->context;
   }
@@ -869,7 +905,7 @@ SILC_CLIENT_CMD_REPLY_FUNC(ping)
 
   SILC_GET16_MSB(status, silc_argument_get_arg_type(cmd->args, 1, NULL));
   if (status != SILC_STATUS_OK) {
-    cmd->client->ops->say(cmd->client, conn, SILC_CLIENT_MESSAGE_ERROR,
+    cmd->client->internal->ops->say(cmd->client, conn, SILC_CLIENT_MESSAGE_ERROR,
             "%s", silc_client_command_status_message(status));
     COMMAND_REPLY_ERROR;
     goto out;
@@ -888,7 +924,7 @@ SILC_CLIENT_CMD_REPLY_FUNC(ping)
       continue;
     if (SILC_ID_SERVER_COMPARE(conn->ping[i].dest_id, id)) {
       diff = curtime - conn->ping[i].start_time;
-      cmd->client->ops->say(cmd->client, conn, SILC_CLIENT_MESSAGE_INFO, 
+      cmd->client->internal->ops->say(cmd->client, conn, SILC_CLIENT_MESSAGE_INFO, 
                            "Ping reply from %s: %d second%s", 
                            conn->ping[i].dest_name, diff, 
                            diff == 1 ? "" : "s");
@@ -934,7 +970,7 @@ SILC_CLIENT_CMD_REPLY_FUNC(join)
   SILC_GET16_MSB(status, silc_argument_get_arg_type(cmd->args, 1, NULL));
   if (status != SILC_STATUS_OK) {
     if (status != SILC_STATUS_ERR_USER_ON_CHANNEL)
-      cmd->client->ops->say(cmd->client, conn, SILC_CLIENT_MESSAGE_ERROR,
+      cmd->client->internal->ops->say(cmd->client, conn, SILC_CLIENT_MESSAGE_ERROR,
                            "%s", silc_client_command_status_message(status));
     COMMAND_REPLY_ERROR;
     goto out;
@@ -942,7 +978,7 @@ SILC_CLIENT_CMD_REPLY_FUNC(join)
 
   argc = silc_argument_get_arg_num(cmd->args);
   if (argc < 7 || argc > 14) {
-    cmd->client->ops->say(cmd->client, conn, SILC_CLIENT_MESSAGE_ERROR,
+    cmd->client->internal->ops->say(cmd->client, conn, SILC_CLIENT_MESSAGE_ERROR,
             "Cannot join channel: Bad reply packet");
     COMMAND_REPLY_ERROR;
     goto out;
@@ -951,7 +987,7 @@ SILC_CLIENT_CMD_REPLY_FUNC(join)
   /* Get channel name */
   tmp = silc_argument_get_arg_type(cmd->args, 2, NULL);
   if (!tmp) {
-    cmd->client->ops->say(cmd->client, conn, SILC_CLIENT_MESSAGE_ERROR,
+    cmd->client->internal->ops->say(cmd->client, conn, SILC_CLIENT_MESSAGE_ERROR,
                          "Cannot join channel: Bad reply packet");
     COMMAND_REPLY_ERROR;
     goto out;
@@ -961,7 +997,7 @@ SILC_CLIENT_CMD_REPLY_FUNC(join)
   /* Get Channel ID */
   tmp = silc_argument_get_arg_type(cmd->args, 3, &len);
   if (!tmp) {
-    cmd->client->ops->say(cmd->client, conn, SILC_CLIENT_MESSAGE_ERROR,
+    cmd->client->internal->ops->say(cmd->client, conn, SILC_CLIENT_MESSAGE_ERROR,
                          "Cannot join channel: Bad reply packet");
     COMMAND_REPLY_ERROR;
     silc_free(channel_name);
@@ -1008,7 +1044,7 @@ SILC_CLIENT_CMD_REPLY_FUNC(join)
   hmac = silc_argument_get_arg_type(cmd->args, 11, NULL);
   if (hmac) {
     if (!silc_hmac_alloc(hmac, NULL, &channel->hmac)) {
-      cmd->client->ops->say(cmd->client, conn, SILC_CLIENT_MESSAGE_ERROR, 
+      cmd->client->internal->ops->say(cmd->client, conn, SILC_CLIENT_MESSAGE_ERROR, 
                            "Cannot join channel: Unsupported HMAC `%s'",
                            hmac);
       COMMAND_REPLY_ERROR;
@@ -1128,7 +1164,7 @@ SILC_CLIENT_CMD_REPLY_FUNC(motd)
   tmp = silc_argument_get_arg_type(cmd->args, 1, NULL);
   SILC_GET16_MSB(status, tmp);
   if (status != SILC_STATUS_OK) {
-    cmd->client->ops->say(cmd->client, conn, SILC_CLIENT_MESSAGE_ERROR,
+    cmd->client->internal->ops->say(cmd->client, conn, SILC_CLIENT_MESSAGE_ERROR,
             "%s", silc_client_command_status_message(status));
     COMMAND_REPLY_ERROR;
     return;
@@ -1158,7 +1194,7 @@ SILC_CLIENT_CMD_REPLY_FUNC(motd)
        if (i == 2)
          line[0] = ' ';
        
-       cmd->client->ops->say(cmd->client, conn, SILC_CLIENT_MESSAGE_INFO,
+       cmd->client->internal->ops->say(cmd->client, conn, SILC_CLIENT_MESSAGE_INFO,
                              "%s", line);
        
        if (!strlen(cp))
@@ -1190,7 +1226,7 @@ SILC_CLIENT_CMD_REPLY_FUNC(umode)
   tmp = silc_argument_get_arg_type(cmd->args, 1, NULL);
   SILC_GET16_MSB(status, tmp);
   if (status != SILC_STATUS_OK) {
-    cmd->client->ops->say(cmd->client, conn, SILC_CLIENT_MESSAGE_ERROR,
+    cmd->client->internal->ops->say(cmd->client, conn, SILC_CLIENT_MESSAGE_ERROR,
             "%s", silc_client_command_status_message(status));
     COMMAND_REPLY_ERROR;
     goto out;
@@ -1230,7 +1266,7 @@ SILC_CLIENT_CMD_REPLY_FUNC(cmode)
 
   SILC_GET16_MSB(status, silc_argument_get_arg_type(cmd->args, 1, NULL));
   if (status != SILC_STATUS_OK) {
-    cmd->client->ops->say(cmd->client, conn, SILC_CLIENT_MESSAGE_ERROR,
+    cmd->client->internal->ops->say(cmd->client, conn, SILC_CLIENT_MESSAGE_ERROR,
             "%s", silc_client_command_status_message(status));
     COMMAND_REPLY_ERROR;
     goto out;
@@ -1295,7 +1331,7 @@ SILC_CLIENT_CMD_REPLY_FUNC(cumode)
   
   SILC_GET16_MSB(status, silc_argument_get_arg_type(cmd->args, 1, NULL));
   if (status != SILC_STATUS_OK) {
-    cmd->client->ops->say(cmd->client, conn, SILC_CLIENT_MESSAGE_ERROR,
+    cmd->client->internal->ops->say(cmd->client, conn, SILC_CLIENT_MESSAGE_ERROR,
             "%s", silc_client_command_status_message(status));
     COMMAND_REPLY_ERROR;
     goto out;
@@ -1384,8 +1420,9 @@ SILC_CLIENT_CMD_REPLY_FUNC(kick)
   tmp = silc_argument_get_arg_type(cmd->args, 1, NULL);
   SILC_GET16_MSB(status, tmp);
   if (status != SILC_STATUS_OK) {
-    cmd->client->ops->say(cmd->client, conn, SILC_CLIENT_MESSAGE_ERROR,
-            "%s", silc_client_command_status_message(status));
+    cmd->client->internal->ops->say(
+                     cmd->client, conn, SILC_CLIENT_MESSAGE_ERROR,
+                     "%s", silc_client_command_status_message(status));
     COMMAND_REPLY_ERROR;
     goto out;
   }
@@ -1409,8 +1446,9 @@ SILC_CLIENT_CMD_REPLY_FUNC(silcoper)
   tmp = silc_argument_get_arg_type(cmd->args, 1, NULL);
   SILC_GET16_MSB(status, tmp);
   if (status != SILC_STATUS_OK) {
-    cmd->client->ops->say(cmd->client, conn, SILC_CLIENT_MESSAGE_ERROR,
-            "%s", silc_client_command_status_message(status));
+    cmd->client->internal->ops->say(
+                      cmd->client, conn, SILC_CLIENT_MESSAGE_ERROR,
+                      "%s", silc_client_command_status_message(status));
     COMMAND_REPLY_ERROR;
     goto out;
   }
@@ -1434,8 +1472,10 @@ SILC_CLIENT_CMD_REPLY_FUNC(oper)
   tmp = silc_argument_get_arg_type(cmd->args, 1, NULL);
   SILC_GET16_MSB(status, tmp);
   if (status != SILC_STATUS_OK) {
-    cmd->client->ops->say(cmd->client, conn, SILC_CLIENT_MESSAGE_ERROR,
-            "%s", silc_client_command_status_message(status));
+    cmd->client->internal->ops->say(
+                      cmd->client, conn, 
+                      SILC_CLIENT_MESSAGE_ERROR,
+                      "%s", silc_client_command_status_message(status));
     COMMAND_REPLY_ERROR;
     goto out;
   }
@@ -1459,8 +1499,9 @@ SILC_CLIENT_CMD_REPLY_FUNC(connect)
   tmp = silc_argument_get_arg_type(cmd->args, 1, NULL);
   SILC_GET16_MSB(status, tmp);
   if (status != SILC_STATUS_OK) {
-    cmd->client->ops->say(cmd->client, conn, SILC_CLIENT_MESSAGE_ERROR,
-            "%s", silc_client_command_status_message(status));
+    cmd->client->internal->ops->say(
+                      cmd->client, conn, SILC_CLIENT_MESSAGE_ERROR,
+                      "%s", silc_client_command_status_message(status));
     COMMAND_REPLY_ERROR;
     goto out;
   }
@@ -1488,8 +1529,9 @@ SILC_CLIENT_CMD_REPLY_FUNC(ban)
   tmp = silc_argument_get_arg_type(cmd->args, 1, NULL);
   SILC_GET16_MSB(status, tmp);
   if (status != SILC_STATUS_OK) {
-    cmd->client->ops->say(cmd->client, conn, SILC_CLIENT_MESSAGE_ERROR,
-            "%s", silc_client_command_status_message(status));
+    cmd->client->internal->ops->say(
+                      cmd->client, conn, SILC_CLIENT_MESSAGE_ERROR,
+                      "%s", silc_client_command_status_message(status));
     COMMAND_REPLY_ERROR;
     goto out;
   }
@@ -1535,8 +1577,9 @@ SILC_CLIENT_CMD_REPLY_FUNC(close)
   tmp = silc_argument_get_arg_type(cmd->args, 1, NULL);
   SILC_GET16_MSB(status, tmp);
   if (status != SILC_STATUS_OK) {
-    cmd->client->ops->say(cmd->client, conn, SILC_CLIENT_MESSAGE_ERROR,
-            "%s", silc_client_command_status_message(status));
+    cmd->client->internal->ops->say(
+                        cmd->client, conn, SILC_CLIENT_MESSAGE_ERROR,
+                        "%s", silc_client_command_status_message(status));
     COMMAND_REPLY_ERROR;
     goto out;
   }
@@ -1560,8 +1603,9 @@ SILC_CLIENT_CMD_REPLY_FUNC(shutdown)
   tmp = silc_argument_get_arg_type(cmd->args, 1, NULL);
   SILC_GET16_MSB(status, tmp);
   if (status != SILC_STATUS_OK) {
-    cmd->client->ops->say(cmd->client, conn, SILC_CLIENT_MESSAGE_ERROR,
-            "%s", silc_client_command_status_message(status));
+    cmd->client->internal->ops->say(
+                        cmd->client, conn, SILC_CLIENT_MESSAGE_ERROR,
+                        "%s", silc_client_command_status_message(status));
     COMMAND_REPLY_ERROR;
     goto out;
   }
@@ -1587,8 +1631,9 @@ SILC_CLIENT_CMD_REPLY_FUNC(leave)
   tmp = silc_argument_get_arg_type(cmd->args, 1, NULL);
   SILC_GET16_MSB(status, tmp);
   if (status != SILC_STATUS_OK) {
-    cmd->client->ops->say(cmd->client, conn, SILC_CLIENT_MESSAGE_ERROR,
-            "%s", silc_client_command_status_message(status));
+    cmd->client->internal->ops->say(
+                         cmd->client, conn, SILC_CLIENT_MESSAGE_ERROR,
+                         "%s", silc_client_command_status_message(status));
     COMMAND_REPLY_ERROR;
     goto out;
   }
@@ -1627,8 +1672,9 @@ SILC_CLIENT_CMD_REPLY_FUNC(users)
   tmp = silc_argument_get_arg_type(cmd->args, 1, NULL);
   SILC_GET16_MSB(status, tmp);
   if (status != SILC_STATUS_OK) {
-    cmd->client->ops->say(cmd->client, conn, SILC_CLIENT_MESSAGE_ERROR,
-            "%s", silc_client_command_status_message(status));
+    cmd->client->internal->ops->say(
+                         cmd->client, conn, SILC_CLIENT_MESSAGE_ERROR,
+                         "%s", silc_client_command_status_message(status));
     COMMAND_REPLY_ERROR;
     goto out;
   }
@@ -1729,7 +1775,6 @@ SILC_CLIENT_CMD_REPLY_FUNC(users)
       if (id_cache && id_cache->context) {
        SilcClientEntry client_entry = (SilcClientEntry)id_cache->context;
        if (client_entry->status & SILC_CLIENT_STATUS_RESOLVING) {
-         client_entry->status &= ~SILC_CLIENT_STATUS_RESOLVING;
          silc_buffer_pull(client_id_list, idp_len);
          silc_buffer_pull(client_mode_list, 4);
          continue;
@@ -1772,9 +1817,12 @@ SILC_CLIENT_CMD_REPLY_FUNC(users)
     SilcBuffer res_cmd;
 
     /* Send the WHOIS command to server */
+    silc_client_command_register(cmd->client, SILC_COMMAND_WHOIS, NULL, NULL,
+                                silc_client_command_reply_whois_i, 0,
+                                ++conn->cmd_ident);
     res_cmd = silc_command_payload_encode(SILC_COMMAND_WHOIS,
                                          res_argc, res_argv, res_argv_lens,
-                                         res_argv_types, ++conn->cmd_ident);
+                                         res_argv_types, conn->cmd_ident);
     silc_client_packet_send(cmd->client, conn->sock, SILC_PACKET_COMMAND, 
                            NULL, 0, NULL, NULL, res_cmd->data, res_cmd->len,
                            TRUE);
@@ -1835,7 +1883,7 @@ SILC_CLIENT_CMD_REPLY_FUNC(getkey)
   tmp = silc_argument_get_arg_type(cmd->args, 1, NULL);
   SILC_GET16_MSB(status, tmp);
   if (status != SILC_STATUS_OK) {
-    cmd->client->ops->say(cmd->client, conn, SILC_CLIENT_MESSAGE_ERROR,
+    cmd->client->internal->ops->say(cmd->client, conn, SILC_CLIENT_MESSAGE_ERROR,
                          "%s", silc_client_command_status_message(status));
     COMMAND_REPLY_ERROR;
     goto out;
@@ -1914,3 +1962,188 @@ SILC_CLIENT_CMD_REPLY_FUNC(getkey)
   silc_free(server_id);
   silc_client_command_reply_free(cmd);
 }
+
+SILC_CLIENT_CMD_REPLY_FUNC(quit)
+{
+  silc_client_command_reply_free(context);
+}
+
+
+/******************************************************************************
+
+                      Internal command reply functions
+
+******************************************************************************/
+
+SILC_CLIENT_CMD_REPLY_FUNC(whois_i)
+{
+  SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
+  SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
+  SilcCommandStatus status;
+
+  SILC_LOG_DEBUG(("Start"));
+
+  SILC_GET16_MSB(status, silc_argument_get_arg_type(cmd->args, 1, NULL));
+  if (status != SILC_STATUS_OK &&
+      status != SILC_STATUS_LIST_START &&
+      status != SILC_STATUS_LIST_ITEM &&
+      status != SILC_STATUS_LIST_END)
+    goto out;
+
+  /* Save WHOIS info */
+  silc_client_command_reply_whois_save(cmd, status, FALSE);
+
+  /* Pending callbacks are not executed if this was an list entry */
+  if (status != SILC_STATUS_OK &&
+      status != SILC_STATUS_LIST_END) {
+    silc_client_command_reply_free(cmd);
+    return;
+  }
+
+ out:
+  SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_WHOIS);
+  SILC_CLIENT_PENDING_DESTRUCTOR(cmd, SILC_COMMAND_WHOIS);
+
+  /* If we received notify for invalid ID we'll remove the ID if we
+     have it cached. */
+  if (status == SILC_STATUS_ERR_NO_SUCH_CLIENT_ID) {
+    SilcClientEntry client_entry;
+    uint32 tmp_len;
+    unsigned char *tmp =
+      silc_argument_get_arg_type(silc_command_get_args(cmd->payload),
+                                2, &tmp_len);
+    if (tmp) {
+      SilcClientID *client_id = silc_id_payload_parse_id(tmp, tmp_len);
+      if (client_id) {
+       client_entry = silc_client_get_client_by_id(cmd->client, conn,
+                                                   client_id);
+       if (client_entry)
+         silc_client_del_client(cmd->client, conn, client_entry);
+       silc_free(client_id);
+      }
+    }
+  }
+
+  /* Unregister this command reply */
+  silc_client_command_unregister(cmd->client, SILC_COMMAND_WHOIS,
+                                NULL, silc_client_command_reply_whois_i,
+                                cmd->ident);
+
+  silc_client_command_reply_free(cmd);
+}
+
+SILC_CLIENT_CMD_REPLY_FUNC(identify_i)
+{
+  SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
+  SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
+  SilcCommandStatus status;
+
+  SILC_LOG_DEBUG(("Start"));
+
+  SILC_GET16_MSB(status, silc_argument_get_arg_type(cmd->args, 1, NULL));
+  if (status != SILC_STATUS_OK &&
+      status != SILC_STATUS_LIST_START &&
+      status != SILC_STATUS_LIST_ITEM &&
+      status != SILC_STATUS_LIST_END)
+    goto out;
+
+  /* Save IDENTIFY info */
+  silc_client_command_reply_identify_save(cmd, status, FALSE);
+
+  /* Pending callbacks are not executed if this was an list entry */
+  if (status != SILC_STATUS_OK &&
+      status != SILC_STATUS_LIST_END) {
+    silc_client_command_reply_free(cmd);
+    return;
+  }
+
+ out:
+  SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_IDENTIFY);
+  SILC_CLIENT_PENDING_DESTRUCTOR(cmd, SILC_COMMAND_IDENTIFY);
+
+  /* If we received notify for invalid ID we'll remove the ID if we
+     have it cached. */
+  if (status == SILC_STATUS_ERR_NO_SUCH_CLIENT_ID) {
+    SilcClientEntry client_entry;
+    uint32 tmp_len;
+    unsigned char *tmp =
+      silc_argument_get_arg_type(silc_command_get_args(cmd->payload),
+                                2, &tmp_len);
+    if (tmp) {
+      SilcClientID *client_id = silc_id_payload_parse_id(tmp, tmp_len);
+      if (client_id) {
+       client_entry = silc_client_get_client_by_id(cmd->client, conn,
+                                                   client_id);
+       if (client_entry)
+         silc_client_del_client(cmd->client, conn, client_entry);
+       silc_free(client_id);
+      }
+    }
+  }
+
+  /* Unregister this command reply */
+  silc_client_command_unregister(cmd->client, SILC_COMMAND_IDENTIFY,
+                                NULL, silc_client_command_reply_identify_i,
+                                cmd->ident);
+
+  silc_client_command_reply_free(cmd);
+}
+
+SILC_CLIENT_CMD_REPLY_FUNC(info_i)
+{
+  SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
+  SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
+  SilcCommandStatus status;
+  unsigned char *tmp;
+  SilcIDCacheEntry id_cache;
+  SilcServerEntry server;
+  SilcServerID *server_id = NULL;
+  char *server_name, *server_info;
+  uint32 len;
+
+  SILC_LOG_DEBUG(("Start"));
+
+  tmp = silc_argument_get_arg_type(cmd->args, 1, NULL);
+  SILC_GET16_MSB(status, tmp);
+  if (status != SILC_STATUS_OK)
+    goto out;
+
+  /* Get server ID */
+  tmp = silc_argument_get_arg_type(cmd->args, 2, &len);
+  if (!tmp)
+    goto out;
+
+  server_id = silc_id_payload_parse_id(tmp, len);
+  if (!server_id)
+    goto out;
+
+  /* Get server name */
+  server_name = silc_argument_get_arg_type(cmd->args, 3, NULL);
+  if (!server_name)
+    goto out;
+
+  /* Get server info */
+  server_info = silc_argument_get_arg_type(cmd->args, 4, NULL);
+  if (!server_info)
+    goto out;
+
+  /* See whether we have this server cached. If not create it. */
+  if (!silc_idcache_find_by_id_one(conn->server_cache, (void *)server_id,
+                                  &id_cache)) {
+    SILC_LOG_DEBUG(("New server entry"));
+    server = silc_calloc(1, sizeof(*server));
+    server->server_name = strdup(server_name);
+    server->server_info = strdup(server_info);
+    server->server_id = silc_id_dup(server_id, SILC_ID_SERVER);
+
+    /* Add it to the cache */
+    silc_idcache_add(conn->server_cache, server->server_name,
+                    server->server_id, (void *)server, 0, NULL);
+  }
+  
+ out:
+  SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_INFO);
+  SILC_CLIENT_PENDING_DESTRUCTOR(cmd, SILC_COMMAND_INFO);
+  silc_free(server_id);
+  silc_client_command_reply_free(cmd);
+}
index 1c50430fed8f6b4f838e9ac97f96744d9ca81ecd..33c412ca485e9c1dd5fd68e8c0452398095e20b3 100644 (file)
@@ -1,16 +1,15 @@
 /*
 
-  command_reply.h
+  command_reply.h 
 
-  Author: Pekka Riikonen <priikone@poseidon.pspt.fi>
+  Author: Pekka Riikonen <priikone@silcnet.org>
 
   Copyright (C) 1997 - 2001 Pekka Riikonen
 
   This program is free software; you can redistribute it and/or modify
   it under the terms of the GNU General Public License as published by
-  the Free Software Foundation; either version 2 of the License, or
-  (at your option) any later version.
-  
+  the Free Software Foundation; version 2 of the License.
+
   This program is distributed in the hope that it will be useful,
   but WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
@@ -27,9 +26,6 @@ typedef struct {
   SilcCommand cmd;
 } SilcClientCommandReply;
 
-/* All client command replys */
-extern SilcClientCommandReply silc_command_reply_list[];
-
 /* Context sent as argument to all command reply functions */
 typedef struct {
   SilcClient client;
@@ -47,12 +43,8 @@ typedef struct {
 
 /* Macros */
 
-/* Macro used for command declaration in command reply list structure */
-#define SILC_CLIENT_CMD_REPLY(func, cmd ) \
-{ silc_client_command_reply_##func, SILC_COMMAND_##cmd }
-
 /* Macro used to declare command reply functions */
-#define SILC_CLIENT_CMD_REPLY_FUNC(func) \
+#define SILC_CLIENT_CMD_REPLY_FUNC(func)                               \
 void silc_client_command_reply_##func(void *context, void *context2)
 
 /* Status message structure. Messages are defined below. */
@@ -66,6 +58,7 @@ typedef struct {
 extern const SilcCommandStatusMessage silc_command_status_messages[];
 
 /* Prototypes */
+
 void silc_client_command_reply_process(SilcClient client,
                                       SilcSocketConnection sock,
                                       SilcPacketContext *packet);
@@ -99,5 +92,11 @@ SILC_CLIENT_CMD_REPLY_FUNC(silcoper);
 SILC_CLIENT_CMD_REPLY_FUNC(leave);
 SILC_CLIENT_CMD_REPLY_FUNC(users);
 SILC_CLIENT_CMD_REPLY_FUNC(getkey);
+SILC_CLIENT_CMD_REPLY_FUNC(quit);
+
+/* Internal command reply functions */
+SILC_CLIENT_CMD_REPLY_FUNC(whois_i);
+SILC_CLIENT_CMD_REPLY_FUNC(identify_i);
+SILC_CLIENT_CMD_REPLY_FUNC(info_i);
 
 #endif
index da646b52255a2f6c3ccd2e7f7cd1d9eb1caea6df..2c520d148031f9cc3a4611145d4dc9fcad805ec0 100644 (file)
 #include "clientlibincludes.h"
 #include "client_internal.h"
 
+/******************************************************************************
+
+                         Client Searching Locally
+
+******************************************************************************/
+
+/* Same as silc_client_get_clients function but does not resolve anything
+   from the server. This checks local cache and returns all matching
+   clients from the local cache. If none was found this returns NULL.
+   The `nickname' is the real nickname of the client, and the `format'
+   is the formatted nickname to find exact match from multiple found
+   entries. The format must be same as given in the SilcClientParams
+   structure to the client library. If the `format' is NULL all found
+   clients by `nickname' are returned. */
+
+SilcClientEntry *silc_client_get_clients_local(SilcClient client,
+                                              SilcClientConnection conn,
+                                              const char *nickname,
+                                              const char *format,
+                                              uint32 *clients_count)
+{
+  SilcIDCacheEntry id_cache;
+  SilcIDCacheList list = NULL;
+  SilcClientEntry entry, *clients;
+  int i = 0;
+  bool found = FALSE;
+
+  /* Find ID from cache */
+  if (!silc_idcache_find_by_name(conn->client_cache, (char *)nickname, &list))
+    return NULL;
+
+  if (!silc_idcache_list_count(list)) {
+    silc_idcache_list_free(list);
+    return NULL;
+  }
+
+  clients = silc_calloc(silc_idcache_list_count(list), sizeof(*clients));
+  *clients_count = silc_idcache_list_count(list);
+
+  if (!format) {
+    /* Take all without any further checking */
+    silc_idcache_list_first(list, &id_cache);
+    while (id_cache) {
+      clients[i++] = id_cache->context;
+      found = TRUE;
+      if (!silc_idcache_list_next(list, &id_cache))
+       break;
+    }
+  } else {
+    /* Check multiple cache entries for match */
+    silc_idcache_list_first(list, &id_cache);
+    while (id_cache) {
+      entry = (SilcClientEntry)id_cache->context;
+      if (strcasecmp(entry->nickname, format)) {
+       if (!silc_idcache_list_next(list, &id_cache)) {
+         break;
+       } else {
+         continue;
+       }
+      }
+      
+      clients[i++] = id_cache->context;
+      found = TRUE;
+      if (!silc_idcache_list_next(list, &id_cache))
+       break;
+    }
+  }
+
+  if (list)
+    silc_idcache_list_free(list);
+
+  if (!found) {
+    *clients_count = 0;
+    if (clients)
+      silc_free(clients);
+    return NULL;
+  }
+
+  return clients;
+}
+
+
+/******************************************************************************
+
+                        Client Resolving from Server
+
+******************************************************************************/
+
 typedef struct {
-  SilcClientCommandContext cmd;
+  SilcClient client;
+  SilcClientConnection conn;
   SilcGetClientCallback completion;
+  void *context;
   char *nickname;
   char *server;
-  void *context;
-  int found;
+  bool found;
 } *GetClientInternal;
 
 SILC_CLIENT_CMD_FUNC(get_client_callback)
@@ -38,11 +127,11 @@ SILC_CLIENT_CMD_FUNC(get_client_callback)
   uint32 clients_count;
 
   /* Get the clients */
-  clients = silc_client_get_clients_local(i->cmd->client, i->cmd->conn,
+  clients = silc_client_get_clients_local(i->client, i->conn,
                                          i->nickname, i->server,
                                          &clients_count);
   if (clients) {
-    i->completion(i->cmd->client, i->cmd->conn, clients, 
+    i->completion(i->client, i->conn, clients, 
                  clients_count, i->context);
     i->found = TRUE;
     silc_free(clients);
@@ -54,13 +143,10 @@ static void silc_client_get_client_destructor(void *context)
   GetClientInternal i = (GetClientInternal)context;
 
   if (i->found == FALSE)
-    i->completion(i->cmd->client, i->cmd->conn, NULL, 0, i->context);
+    i->completion(i->client, i->conn, NULL, 0, i->context);
 
-  silc_client_command_free(i->cmd);
-  if (i->nickname)
-    silc_free(i->nickname);
-  if (i->server)
-    silc_free(i->server);
+  silc_free(i->nickname);
+  silc_free(i->server);
   silc_free(i);
 }
 
@@ -80,113 +166,130 @@ void silc_client_get_clients(SilcClient client,
                             SilcGetClientCallback completion,
                             void *context)
 {
-  char ident[512];
-  SilcClientCommandContext ctx;
-  GetClientInternal i = silc_calloc(1, sizeof(*i));
-      
-  /* No ID found. Do query from the server. The query is done by 
-     sending simple IDENTIFY command to the server. */
-  ctx = silc_client_command_alloc();
-  ctx->client = client;
-  ctx->conn = conn;
-  ctx->command = silc_client_command_find("IDENTIFY");
-  memset(ident, 0, sizeof(ident));
-  snprintf(ident, sizeof(ident), "IDENTIFY %s", nickname);
-  silc_parse_command_line(ident, &ctx->argv, &ctx->argv_lens, 
-                         &ctx->argv_types, &ctx->argc, 2);
-
-  i->cmd = silc_client_command_dup(ctx);
-  i->nickname = nickname ? strdup(nickname) : NULL;
+  GetClientInternal i;
+  char *userhost;
+
+  if (!nickname)
+    return;
+
+  i = silc_calloc(1, sizeof(*i));
+  i->client = client;
+  i->conn = conn;
+  i->nickname = strdup(nickname);
   i->server = server ? strdup(server) : NULL;
   i->completion = completion;
   i->context = context;
 
-  /* Call the command */
-  ctx->command->cb(ctx, NULL);
+  if (nickname && server) {
+    userhost = silc_calloc(strlen(nickname) + strlen(server) + 2,
+                          sizeof(*userhost));
+    strncat(userhost, nickname, strlen(nickname));
+    strncat(userhost, "@", 1);
+    strncat(userhost, server, strlen(server));
+  } else {
+    userhost = strdup(nickname);
+  }
+
+  /* Register our own command reply for this command */
+  silc_client_command_register(client, SILC_COMMAND_IDENTIFY, NULL, NULL,
+                              silc_client_command_reply_identify_i, 0,
+                              ++conn->cmd_ident);
+
+  /* Send the command */
+  silc_client_command_send(client, conn, SILC_COMMAND_IDENTIFY, 
+                          conn->cmd_ident, 1, 1, userhost, 
+                          strlen(userhost));
 
   /* Add pending callback */
-  silc_client_command_pending(conn, SILC_COMMAND_IDENTIFY, 
-                             conn->cmd_ident, 
+  silc_client_command_pending(conn, SILC_COMMAND_IDENTIFY, conn->cmd_ident,
                              silc_client_get_client_destructor,
                              silc_client_command_get_client_callback, 
                              (void *)i);
+
+  silc_free(userhost);
 }
 
-/* Same as silc_client_get_clients function but does not resolve anything
-   from the server. This checks local cache and returns all matching
-   clients from the local cache. If none was found this returns NULL.
-   The `nickname' is the real nickname of the client, and the `format'
-   is the formatted nickname to find exact match from multiple found
-   entries. The format must be same as given in the SilcClientParams
-   structure to the client library. If the `format' is NULL all found
-   clients by `nickname' are returned. */
+/* The old style function to find client entry. This is used by the
+   library internally. If `query' is TRUE then the client information is
+   requested by the server. The pending command callback must be set
+   by the caller. */
+/* XXX This function should be removed */
 
-SilcClientEntry *silc_client_get_clients_local(SilcClient client,
-                                              SilcClientConnection conn,
-                                              const char *nickname,
-                                              const char *format,
-                                              uint32 *clients_count)
+SilcClientEntry silc_idlist_get_client(SilcClient client,
+                                      SilcClientConnection conn,
+                                      const char *nickname,
+                                      const char *format,
+                                      bool query)
 {
   SilcIDCacheEntry id_cache;
   SilcIDCacheList list = NULL;
-  SilcClientEntry entry, *clients;
-  int i = 0;
-  bool found = FALSE;
+  SilcClientEntry entry = NULL;
+
+  SILC_LOG_DEBUG(("Start"));
 
   /* Find ID from cache */
-  if (!silc_idcache_find_by_name(conn->client_cache, (char *)nickname, &list))
-    return NULL;
+  if (!silc_idcache_find_by_name(conn->client_cache, (char *)nickname, 
+                                &list)) {
+  identify:
 
-  if (!silc_idcache_list_count(list)) {
-    silc_idcache_list_free(list);
+    if (query) {
+      SILC_LOG_DEBUG(("Requesting Client ID from server"));
+      
+      /* Register our own command reply for this command */
+      silc_client_command_register(client, SILC_COMMAND_IDENTIFY, NULL, NULL,
+                                  silc_client_command_reply_identify_i, 0,
+                                  ++conn->cmd_ident);
+
+      /* Send the command */
+      silc_client_command_send(client, conn, SILC_COMMAND_IDENTIFY, 
+                              conn->cmd_ident, 1, 1, nickname,
+                              strlen(nickname));
+
+      if (list)
+       silc_idcache_list_free(list);
+
+      return NULL;
+    }
     return NULL;
   }
 
-  clients = silc_calloc(silc_idcache_list_count(list), sizeof(*clients));
-  *clients_count = silc_idcache_list_count(list);
-
   if (!format) {
-    /* Take all without any further checking */
-    silc_idcache_list_first(list, &id_cache);
-    while (id_cache) {
-      clients[i++] = id_cache->context;
-      found = TRUE;
-      if (!silc_idcache_list_next(list, &id_cache))
-       break;
-    }
+    /* Take first found cache entry */
+    if (!silc_idcache_list_first(list, &id_cache))
+      goto identify;
+
+    entry = (SilcClientEntry)id_cache->context;
   } else {
     /* Check multiple cache entries for match */
     silc_idcache_list_first(list, &id_cache);
     while (id_cache) {
       entry = (SilcClientEntry)id_cache->context;
+
       if (strcasecmp(entry->nickname, format)) {
        if (!silc_idcache_list_next(list, &id_cache)) {
+         entry = NULL;
          break;
        } else {
+         entry = NULL;
          continue;
        }
       }
-      
-      clients[i++] = id_cache->context;
-      found = TRUE;
-      if (!silc_idcache_list_next(list, &id_cache))
-       break;
+
+      break;
     }
+
+    /* If match weren't found, request it */
+    if (!entry)
+      goto identify;
   }
 
   if (list)
     silc_idcache_list_free(list);
 
-  if (!found) {
-    *clients_count = 0;
-    if (clients)
-      silc_free(clients);
-    return NULL;
-  }
-
-  return clients;
+  return entry;
 }
 
+
 typedef struct {
   SilcClient client;
   SilcClientConnection conn;
@@ -356,8 +459,13 @@ void silc_client_get_clients_by_list(SilcClient client,
                            NULL, 0, NULL, NULL, res_cmd->data, res_cmd->len,
                            TRUE);
 
-    silc_client_command_pending(conn, SILC_COMMAND_IDENTIFY, 
-                               conn->cmd_ident, 
+    /* Register our own command reply for this command */
+    silc_client_command_register(client, SILC_COMMAND_IDENTIFY, NULL, NULL,
+                                silc_client_command_reply_identify_i, 0,
+                                conn->cmd_ident);
+
+    /* Process the applications request after reply has been received  */
+    silc_client_command_pending(conn, SILC_COMMAND_IDENTIFY, conn->cmd_ident,
                                silc_client_get_clients_list_destructor,
                                silc_client_command_get_clients_list_callback, 
                                (void *)in);
@@ -378,90 +486,6 @@ void silc_client_get_clients_by_list(SilcClient client,
   silc_client_command_get_clients_list_callback((void *)in, NULL);
 }
 
-/* The old style function to find client entry. This is used by the
-   library internally. If `query' is TRUE then the client information is
-   requested by the server. The pending command callback must be set
-   by the caller. */
-
-SilcClientEntry silc_idlist_get_client(SilcClient client,
-                                      SilcClientConnection conn,
-                                      const char *nickname,
-                                      const char *format,
-                                      bool query)
-{
-  SilcIDCacheEntry id_cache;
-  SilcIDCacheList list = NULL;
-  SilcClientEntry entry = NULL;
-
-  SILC_LOG_DEBUG(("Start"));
-
-  /* Find ID from cache */
-  if (!silc_idcache_find_by_name(conn->client_cache, (char *)nickname, 
-                                &list)) {
-  identify:
-
-    if (query) {
-      char ident[512];
-      SilcClientCommandContext ctx;
-      
-      SILC_LOG_DEBUG(("Requesting Client ID from server"));
-      
-      /* No ID found. Do query from the server. The query is done by 
-        sending simple IDENTIFY command to the server. */
-      ctx = silc_client_command_alloc();
-      ctx->client = client;
-      ctx->conn = conn;
-      ctx->command = silc_client_command_find("IDENTIFY");
-      memset(ident, 0, sizeof(ident));
-      snprintf(ident, sizeof(ident), "IDENTIFY %s", nickname);
-      silc_parse_command_line(ident, &ctx->argv, &ctx->argv_lens, 
-                             &ctx->argv_types, &ctx->argc, 2);
-      ctx->command->cb(ctx, NULL);
-      
-      if (list)
-       silc_idcache_list_free(list);
-
-      return NULL;
-    }
-    return NULL;
-  }
-
-  if (!format) {
-    /* Take first found cache entry */
-    if (!silc_idcache_list_first(list, &id_cache))
-      goto identify;
-
-    entry = (SilcClientEntry)id_cache->context;
-  } else {
-    /* Check multiple cache entries for match */
-    silc_idcache_list_first(list, &id_cache);
-    while (id_cache) {
-      entry = (SilcClientEntry)id_cache->context;
-
-      if (strcasecmp(entry->nickname, format)) {
-       if (!silc_idcache_list_next(list, &id_cache)) {
-         entry = NULL;
-         break;
-       } else {
-         entry = NULL;
-         continue;
-       }
-      }
-
-      break;
-    }
-
-    /* If match weren't found, request it */
-    if (!entry)
-      goto identify;
-  }
-
-  if (list)
-    silc_idcache_list_free(list);
-
-  return entry;
-}
-
 /* Finds entry for client by the client's ID. Returns the entry or NULL
    if the entry was not found. */
 
@@ -536,26 +560,38 @@ void silc_client_get_client_by_id_resolve(SilcClient client,
 
   SILC_LOG_DEBUG(("Start"));
 
-  idp = silc_id_payload_encode(client_id, SILC_ID_CLIENT);
-  silc_client_send_command(client, conn, SILC_COMMAND_WHOIS, 
-                          ++conn->cmd_ident,
-                          1, 3, idp->data, idp->len);
-  silc_buffer_free(idp);
-
   i->client = client;
   i->conn = conn;
   i->client_id = silc_id_dup(client_id, SILC_ID_CLIENT);
   i->completion = completion;
   i->context = context;
       
+  /* Register our own command reply for this command */
+  silc_client_command_register(client, SILC_COMMAND_WHOIS, NULL, NULL,
+                              silc_client_command_reply_whois_i, 0,
+                              ++conn->cmd_ident);
+
+  /* Send the command */
+  idp = silc_id_payload_encode(client_id, SILC_ID_CLIENT);
+  silc_client_command_send(client, conn, SILC_COMMAND_WHOIS, conn->cmd_ident,
+                          1, 3, idp->data, idp->len);
+  silc_buffer_free(idp);
+
   /* Add pending callback */
-  silc_client_command_pending(conn, SILC_COMMAND_WHOIS, 
-                             conn->cmd_ident, 
+  silc_client_command_pending(conn, SILC_COMMAND_WHOIS, conn->cmd_ident,
                              silc_client_get_client_by_id_destructor,
                              silc_client_command_get_client_by_id_callback, 
                              (void *)i);
 }
 
+
+/******************************************************************************
+
+                Client, Channel and Server entry manipulation
+
+******************************************************************************/
+
+
 /* Creates new client entry and adds it to the ID cache. Returns pointer
    to the new entry. */
 
@@ -785,21 +821,26 @@ void silc_client_get_channel_by_id_resolve(SilcClient client,
 
   SILC_LOG_DEBUG(("Start"));
 
-  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;
       
+  /* Register our own command reply for this command */
+  silc_client_command_register(client, SILC_COMMAND_IDENTIFY, NULL, NULL,
+                              silc_client_command_reply_identify_i, 0,
+                              ++conn->cmd_ident);
+
+  /* Send the command */
+  idp = silc_id_payload_encode(channel_id, SILC_ID_CHANNEL);
+  silc_client_command_send(client, conn, SILC_COMMAND_IDENTIFY, 
+                          conn->cmd_ident,
+                          1, 5, idp->data, idp->len);
+  silc_buffer_free(idp);
+
   /* Add pending callback */
-  silc_client_command_pending(conn, SILC_COMMAND_IDENTIFY, 
-                             conn->cmd_ident, 
+  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);
@@ -822,9 +863,15 @@ SilcChannelEntry silc_idlist_get_channel_by_id(SilcClient client,
     return channel;
 
   if (query) {
+    /* Register our own command reply for this command */
+    silc_client_command_register(client, SILC_COMMAND_IDENTIFY, NULL, NULL,
+                                silc_client_command_reply_identify_i, 0,
+                                ++conn->cmd_ident);
+
+    /* Send the command */
     idp = silc_id_payload_encode(channel_id, SILC_ID_CHANNEL);
-    silc_client_send_command(client, conn, SILC_COMMAND_IDENTIFY, 
-                            ++conn->cmd_ident,
+    silc_client_command_send(client, conn, SILC_COMMAND_IDENTIFY, 
+                            conn->cmd_ident,
                             1, 5, idp->data, idp->len);
     silc_buffer_free(idp);
   }
@@ -902,7 +949,7 @@ void silc_client_nickname_format(SilcClient client,
 
   SILC_LOG_DEBUG(("Start"));
 
-  if (!client->params->nickname_format[0])
+  if (!client->internal->params->nickname_format[0])
     return;
 
   if (!client_entry->nickname)
@@ -914,7 +961,7 @@ void silc_client_nickname_format(SilcClient client,
   clients = silc_client_get_clients_local(client, conn,
                                          client_entry->nickname, NULL,
                                          &clients_count);
-  if (!clients && !client->params->nickname_force_format)
+  if (!clients && !client->internal->params->nickname_force_format)
     return;
 
   len = 0;
@@ -924,7 +971,7 @@ void silc_client_nickname_format(SilcClient client,
   if (!len)
     return;
 
-  cp = client->params->nickname_format;
+  cp = client->internal->params->nickname_format;
   while (*cp) {
     if (*cp == '%') {
       cp++;
index f70506e936df572d4f2fa9b2bc75092c94c189be..41daced5cab0a1e734df60b23af793ab2fad3e9c 100644 (file)
@@ -97,10 +97,10 @@ void silc_client_protocol_ke_verify_key(SilcSKE ske,
   verify->completion_context = completion_context;
 
   /* Verify public key from user. */
-  client->ops->verify_public_key(client, ctx->sock->user_data, 
-                                ctx->sock->type,
-                                pk_data, pk_len, pk_type,
-                                silc_client_verify_key_cb, verify);
+  client->internal->ops->verify_public_key(client, ctx->sock->user_data, 
+                                          ctx->sock->type,
+                                          pk_data, pk_len, pk_type,
+                                          silc_client_verify_key_cb, verify);
 }
 
 /* Sets the negotiated key material into use for particular connection. */
@@ -197,7 +197,7 @@ SilcSKEStatus silc_ske_check_version(SilcSKE ske, unsigned char *version,
   if (cp)
     build = atoi(cp + 1);
 
-  cp = client->silc_client_version + 9;
+  cp = client->internal->silc_client_version + 9;
   if (!cp)
     status = SILC_SKE_STATUS_BAD_VERSION;
 
@@ -219,8 +219,9 @@ SilcSKEStatus silc_ske_check_version(SilcSKE ske, unsigned char *version,
     ske->backward_version = 1;
 
   if (status != SILC_SKE_STATUS_OK)
-    client->ops->say(client, conn, SILC_CLIENT_MESSAGE_AUDIT,
-                    "We don't support server version `%s'", version);
+    client->internal->ops->say(client, conn, SILC_CLIENT_MESSAGE_AUDIT,
+                              "We don't support server version `%s'", 
+                              version);
 
   return status;
 }
@@ -245,7 +246,8 @@ static void silc_client_protocol_ke_continue(SilcSKE ske,
 
   if (ske->status != SILC_SKE_STATUS_OK) {
     /* Call failure client operation */
-    client->ops->failure(client, conn, protocol, (void *)ske->status);
+    client->internal->ops->failure(client, conn, protocol, 
+                                  (void *)ske->status);
     protocol->state = SILC_PROTOCOL_STATE_ERROR;
     silc_protocol_execute(protocol, client->schedule, 0, 0);
     return;
@@ -311,16 +313,18 @@ SILC_TASK_CALLBACK(silc_client_protocol_key_exchange)
       if (ctx->responder == TRUE) {
        /* Start the key exchange by processing the received security
           properties packet from initiator. */
-       status = silc_ske_responder_start(ske, ctx->rng, ctx->sock,
-                                         client->silc_client_version,
-                                         ctx->packet->buffer, TRUE);
+       status = 
+         silc_ske_responder_start(ske, ctx->rng, ctx->sock,
+                                  client->internal->silc_client_version,
+                                  ctx->packet->buffer, TRUE);
       } else {
        SilcSKEStartPayload *start_payload;
 
        /* Assemble security properties. */
-       silc_ske_assemble_security_properties(ske, SILC_SKE_SP_FLAG_MUTUAL, 
-                                             client->silc_client_version,
-                                             &start_payload);
+       silc_ske_assemble_security_properties(
+                                 ske, SILC_SKE_SP_FLAG_MUTUAL, 
+                                 client->internal->silc_client_version,
+                                 &start_payload);
 
        /* Start the key exchange by sending our security properties
           to the remote end. */
@@ -449,11 +453,13 @@ SILC_TASK_CALLBACK(silc_client_protocol_key_exchange)
 
       if (status != SILC_SKE_STATUS_OK) {
         if (status == SILC_SKE_STATUS_UNSUPPORTED_PUBLIC_KEY) {
-          client->ops->say(client, conn, SILC_CLIENT_MESSAGE_AUDIT, 
-                          "Received unsupported server %s public key",
-                          ctx->sock->hostname);
+          client->internal->ops->say(
+                            client, conn, SILC_CLIENT_MESSAGE_AUDIT, 
+                            "Received unsupported server %s public key",
+                            ctx->sock->hostname);
         } else {
-          client->ops->say(client, conn, SILC_CLIENT_MESSAGE_AUDIT,
+          client->internal->ops->say(
+                          client, conn, SILC_CLIENT_MESSAGE_AUDIT,
                           "Error during key exchange protocol with server %s",
                           ctx->sock->hostname);
         }
@@ -653,12 +659,13 @@ SILC_TASK_CALLBACK(silc_client_protocol_connection_auth)
          break;
        }
 
-       client->ops->say(client, conn, SILC_CLIENT_MESSAGE_INFO,
-                        "Password authentication required by server %s",
-                        ctx->sock->hostname);
-       client->ops->ask_passphrase(client, conn,
-                                   silc_client_conn_auth_continue,
-                                   protocol);
+       client->internal->ops->say(
+                       client, conn, SILC_CLIENT_MESSAGE_INFO,
+                       "Password authentication required by server %s",
+                       ctx->sock->hostname);
+       client->internal->ops->ask_passphrase(client, conn,
+                                             silc_client_conn_auth_continue,
+                                             protocol);
        return;
        break;
 
index cb48fd93d419037555bb0cbfc2c88aafa0299b4a..11851aff98a354b3aa275f0e2a7fb2da8f78c7c9 100644 (file)
@@ -1123,7 +1123,7 @@ bool silc_client_del_server(SilcClient client, SilcClientConnection conn,
  *
  * SYNOPSIS
  *
- *    SilcClientCommandContext silc_client_command_alloc();
+ *    SilcClientCommandContext silc_client_command_alloc(void);
  *
  * DESCRIPTION
  *
@@ -1134,7 +1134,7 @@ bool silc_client_del_server(SilcClient client, SilcClientConnection conn,
  *    context. 
  *
  ***/
-SilcClientCommandContext silc_client_command_alloc();
+SilcClientCommandContext silc_client_command_alloc(void);
 
 /****f* silcclient/SilcClientAPI/silc_client_command_free
  *
@@ -1171,7 +1171,8 @@ SilcClientCommandContext silc_client_command_dup(SilcClientCommandContext ctx);
  *
  * SYNOPSIS
  *
- *    SilcClientCommand *silc_client_command_find(const char *name);
+ *    SilcClientCommand silc_client_command_find(SilcClient client,
+ *                                               const char *name);
  *
  * DESCRIPTION
  *
@@ -1179,13 +1180,36 @@ SilcClientCommandContext silc_client_command_dup(SilcClientCommandContext ctx);
  *    command is not found. See the `command.[ch]' for the command list. 
  *
  ***/
-SilcClientCommand *silc_client_command_find(const char *name);
+SilcClientCommand silc_client_command_find(SilcClient client,
+                                          const char *name);
 
-/****f* silcclient/SilcClientAPI/silc_client_send_command
+/****f* silcclient/SilcClientAPI/silc_client_command_call
  *
  * SYNOPSIS
  *
- *    void silc_client_send_command(SilcClient client, 
+ *    void silc_client_command_call(SilcClientCommand command);
+ *
+ * DESCRIPTION
+ *
+ *    Calls the command (executes it).  Application can call this after
+ *    it has allocated the SilcClientCommandContext with the function
+ *    silc_client_command_alloc and found the command from the client
+ *    library by calling silc_client_command_find.  This will execute
+ *    the command.
+ *
+ *    Application can call the command function directly too if it
+ *    wishes to do so.  See the command.h for details of the
+ *    SilcClientCommand structure.
+ *
+ ***/
+void silc_client_command_call(SilcClientCommand command,
+                             SilcClientCommandContext cmd);
+
+/****f* silcclient/SilcClientAPI/silc_client_command_send
+ *
+ * SYNOPSIS
+ *
+ *    void silc_client_command_send(SilcClient client, 
  *                                  SilcClientConnection conn,
  *                                  SilcCommand command, uint16 ident,
  *                                  uint32 argc, ...);
@@ -1193,10 +1217,13 @@ SilcClientCommand *silc_client_command_find(const char *name);
  * DESCRIPTION
  *
  *    Generic function to send any command. The arguments must be sent already
- *    encoded into correct form and in correct order. 
+ *    encoded into correct form and in correct order. If application wants
+ *    to perform the commands by itself, it can do so and send the data
+ *    directly to the server using this function.  If application is using
+ *    the silc_client_command_call, this function is usually not used.
  *
  ***/
-void silc_client_send_command(SilcClient client, SilcClientConnection conn,
+void silc_client_command_send(SilcClient client, SilcClientConnection conn,
                              SilcCommand command, uint16 ident,
                              uint32 argc, ...);
 
@@ -1232,10 +1259,14 @@ typedef void (*SilcClientPendingDestructor)(void *context);
  *
  *    Add new pending command to be executed when reply to a command has been
  *    received.  The `reply_cmd' is the command that will call the `callback'
- *    with `context' when reply has been received.  If `ident is non-zero
+ *    with `context' when reply has been received.  If `ident' is non-zero
  *    the `callback' will be executed when received reply with command 
  *    identifier `ident'. 
  *
+ *    Note that the application is notified about the received command
+ *    reply through the `command_reply' client operation before calling
+ *    the `callback` pending command callback.
+ *
  ***/
 void silc_client_command_pending(SilcClientConnection conn,
                                 SilcCommand reply_cmd,
diff --git a/prepare b/prepare
index bbd966f4701d354c1cd113ca41f311a58126a4c1..db3d2eb83c883b0fb7b1432979174e4e12dec60c 100755 (executable)
--- a/prepare
+++ b/prepare
@@ -37,7 +37,7 @@
 # SILC Distribution versions. Set here or give the version on the command
 # line as argument.
 #
-SILC_VERSION=0.7                       # Base version
+SILC_VERSION=0.7.1                     # Base version
 
 #############################################################################
 
index 94ec41a07396a2c1f337a37851c4edf8b0592327..119767b3cab4f5a51728d08f687f76b6fff9b1de 100644 (file)
@@ -49,6 +49,13 @@ o Library
          and thus not require any memory allocation.  Same will happen
          with silc_id_payload_* functions.
 
+       o Rewrite the lib/silcutil/silcprotocol.[ch] not to have 
+         [un]register functions, but to make it context based all
+         the way.  The alloc should take as argument the protocol      
+         type and its callback (not only final callback).  It is not
+         good that we have now global list of registered protocols.
+
+
 o Server
 
        o When processing the decrypted and parsed packet we call the