Initial client library rewrite, connects to remote server already.
authorPekka Riikonen <priikone@silcnet.org>
Sat, 11 Nov 2006 17:18:44 +0000 (17:18 +0000)
committerPekka Riikonen <priikone@silcnet.org>
Sat, 11 Nov 2006 17:18:44 +0000 (17:18 +0000)
See TODO for everything still to do.

25 files changed:
lib/silcclient/Makefile.ad
lib/silcclient/README
lib/silcclient/client.c
lib/silcclient/client.h
lib/silcclient/client_attrs.c
lib/silcclient/client_channel.c
lib/silcclient/client_connect.c [new file with mode: 0644]
lib/silcclient/client_connect.h [new file with mode: 0644]
lib/silcclient/client_entry.c [moved from lib/silcclient/idlist.c with 57% similarity]
lib/silcclient/client_entry.h [moved from lib/silcclient/idlist.h with 73% similarity]
lib/silcclient/client_internal.h
lib/silcclient/client_notify.c
lib/silcclient/client_prvmsg.c
lib/silcclient/client_prvmsg.h [new file with mode: 0644]
lib/silcclient/client_register.c [new file with mode: 0644]
lib/silcclient/client_register.h [new file with mode: 0644]
lib/silcclient/command.c
lib/silcclient/command.h
lib/silcclient/command_reply.c
lib/silcclient/command_reply.h
lib/silcclient/protocol.c
lib/silcclient/silcclient.h
lib/silcclient/silcclient_entry.h [new file with mode: 0644]
lib/silcclient/tests/Makefile.am [new file with mode: 0644]
lib/silcclient/tests/test_silcclient.c [new file with mode: 0644]

index b85ac5a9f083af64a9c746681ad9cfa08c4ddbfc..63027fc51eff6b849560941691f3f34413ab1b0e 100644 (file)
@@ -3,7 +3,7 @@
 #
 #  Author: Pekka Riikonen <priikone@silcnet.org>
 #
-#  Copyright (C) 2000 - 2005 Pekka Riikonen
+#  Copyright (C) 2000 - 2006 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
@@ -19,27 +19,17 @@ AUTOMAKE_OPTIONS = 1.0 no-dependencies foreign
 
 noinst_LTLIBRARIES = libsilcclient.la
 
-libsilcclient_la_SOURCES = \
-       client.c \
-       client_keyagr.c \
-       client_notify.c \
-       client_prvmsg.c \
-       client_channel.c \
-       client_ftp.c    \
-       client_resume.c \
-       client_attrs.c  \
-       command.c \
-       command_reply.c \
-       idlist.c \
-       protocol.c
+libsilcclient_la_SOURCES =             \
+       client.c                        \
+       client_entry.c                  \
+       client_prvmsg.c                 \
+       client_connect.c                \
+       client_register.c               \
+       command.c                       \
+       command_reply.c
 
 #ifdef SILC_DIST_TOOLKIT
 include_HEADERS=       \
-       client.h        \
-       command.h       \
-       command_reply.h \
-       idlist.h        \
-       protocol.h      \
        silcclient.h
 
 SILC_EXTRA_DIST = client_ops_example.c
index fef8dd769fef487af0e3cf8177a973fb4b496d6c..a9c9d485bf64d21034f256db6f7d59e788607944 100644 (file)
@@ -61,8 +61,7 @@ Using FSM
    set the old SilcPacket to the packet thread's state context and move back
    to the packet processor with silc_fsm_next and silc_fsm_continue_sync
    (always synchronously, never async).  This design may change later,
-   but for now this is it.  This also means that SILC_FSM_CALL must not
-   be used in packet processors because it returns SILC_FSM_WAIT.
+   but for now this is it.
 
 
 When to use FSM semaphore signalling?
index e4191d37ee35d1cd48d508ce36f4091f9c5b5492..8834c272f92278f5a263594520cc3e9ac9e6d89a 100644 (file)
@@ -4,7 +4,7 @@
 
   Author: Pekka Riikonen <priikone@silcnet.org>
 
-  Copyright (C) 1997 - 2005 Pekka Riikonen
+  Copyright (C) 1997 - 2006 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
 #include "silcclient.h"
 #include "client_internal.h"
 
-/* Static task callback prototypes */
-SILC_TASK_CALLBACK(silc_client_connect_to_server_start);
-SILC_TASK_CALLBACK(silc_client_connect_to_server_second);
-SILC_TASK_CALLBACK(silc_client_connect_to_server_final);
-SILC_TASK_CALLBACK(silc_client_rekey_final);
+/************************** Types and definitions ***************************/
 
-static SilcBool silc_client_packet_parse(SilcPacketParserContext *parser_context,
-                                    void *context);
-static void silc_client_packet_parse_type(SilcClient client,
-                                         SilcSocketConnection sock,
-                                         SilcPacketContext *packet);
-void silc_client_resolve_auth_method(SilcBool success,
-                                    SilcProtocolAuthMeth auth_meth,
-                                    const unsigned char *auth_data,
-                                    SilcUInt32 auth_data_len, void *context);
+SILC_FSM_STATE(silc_client_connection_st_run);
+SILC_FSM_STATE(silc_client_new_id);
 
-/* Allocates new client object. This has to be done before client may
-   work. After calling this one must call silc_client_init to initialize
-   the client. The `application' is application specific user data pointer
-   and caller must free it. */
 
-SilcClient silc_client_alloc(SilcClientOperations *ops,
-                            SilcClientParams *params,
-                            void *application,
-                            const char *version_string)
+/************************ Static utility functions **************************/
+
+/* Packet engine callback to receive a packet */
+
+static SilcBool silc_client_packet_receive(SilcPacketEngine engine,
+                                          SilcPacketStream stream,
+                                          SilcPacket packet,
+                                          void *callback_context,
+                                          void *stream_context)
 {
-  SilcClient new_client;
+  SilcClient client = callback_context;
+  SilcClientConnection conn = stream_context;
 
-  new_client = silc_calloc(1, sizeof(*new_client));
-  new_client->application = application;
+  /* Packets we do not handle */
+  switch (packet->type) {
+  case SILC_PACKET_HEARTBEAT:
+  case SILC_PACKET_SUCCESS:
+  case SILC_PACKET_FAILURE:
+  case SILC_PACKET_REJECT:
+  case SILC_PACKET_KEY_EXCHANGE:
+  case SILC_PACKET_KEY_EXCHANGE_1:
+  case SILC_PACKET_KEY_EXCHANGE_2:
+  case SILC_PACKET_REKEY:
+  case SILC_PACKET_REKEY_DONE:
+  case SILC_PACKET_CONNECTION_AUTH:
+  case SILC_PACKET_CONNECTION_AUTH_REQUEST:
+    return FALSE;
+    break;
+  }
 
-  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));
-  if (!version_string)
-    version_string = silc_version_string;
-  new_client->internal->silc_client_version = strdup(version_string);
+  /* Signal packet processor thread for a new packet */
+  conn->internal->new_packet = TRUE;
+  silc_fsm_set_state_context(&conn->internal->packet_thread, packet);
+  silc_fsm_continue_sync(&conn->internal->packet_thread);
 
-  if (params)
-    memcpy(new_client->internal->params, params, sizeof(*params));
+  return TRUE;
+}
 
-  if (!new_client->internal->params->task_max)
-    new_client->internal->params->task_max = 200;
+/* Packet engine callback to indicate end of stream */
 
-  if (!new_client->internal->params->rekey_secs)
-    new_client->internal->params->rekey_secs = 3600;
+static void silc_client_packet_eos(SilcPacketEngine engine,
+                                  SilcPacketStream stream,
+                                  void *callback_context,
+                                  void *stream_context)
+{
+  SILC_LOG_DEBUG(("End of stream received"));
+}
 
-  if (!new_client->internal->params->connauth_request_secs)
-    new_client->internal->params->connauth_request_secs = 2;
+/* Packet engine callback to indicate error */
 
-  new_client->internal->params->
-    nickname_format[sizeof(new_client->internal->
-                          params->nickname_format) - 1] = 0;
+static void silc_client_packet_error(SilcPacketEngine engine,
+                                    SilcPacketStream stream,
+                                    SilcPacketError error,
+                                    void *callback_context,
+                                    void *stream_context)
+{
 
-  return new_client;
 }
 
-/* Frees client object and its internals. */
+/* Packet stream callbacks */
+static SilcPacketCallbacks silc_client_stream_cbs =
+{
+  silc_client_packet_receive,
+  silc_client_packet_eos,
+  silc_client_packet_error
+};
 
-void silc_client_free(SilcClient client)
+/* FSM destructor */
+
+void silc_client_fsm_destructor(SilcFSM fsm, void *fsm_context,
+                               void *destructor_context)
 {
-  if (client) {
-    if (client->rng)
-      silc_rng_free(client->rng);
+  silc_fsm_free(fsm);
+}
 
-    if (!client->internal->params->dont_register_crypto_library) {
-      silc_cipher_unregister_all();
-      silc_pkcs_unregister_all();
-      silc_hash_unregister_all();
-      silc_hmac_unregister_all();
-    }
 
-    silc_hash_free(client->md5hash);
-    silc_hash_free(client->sha1hash);
-    silc_hmac_free(client->internal->md5hmac);
-    silc_hmac_free(client->internal->sha1hmac);
-    silc_cipher_free(client->internal->none_cipher);
-    silc_free(client->internal->params);
-    silc_free(client->internal->silc_client_version);
-    silc_free(client->internal);
-    silc_free(client);
-  }
+/************************** Connection's machine ****************************/
+
+/* Start the connection's state machine.  If threads are in use the machine
+   is always executed in a real thread. */
+
+SILC_FSM_STATE(silc_client_connection_st_start)
+{
+  SilcClientConnection conn = fsm_context;
+
+  /* Take scheduler for connection */
+  conn->internal->schedule = silc_fsm_get_schedule(fsm);
+
+  /*** Run connection machine */
+  silc_fsm_init(&conn->internal->fsm, conn, NULL, NULL,
+               conn->internal->schedule);
+  silc_fsm_sema_init(&conn->internal->wait_event, &conn->internal->fsm, 0);
+  silc_fsm_start_sync(&conn->internal->fsm, silc_client_connection_st_run);
+
+  /*** Run packet processor FSM thread */
+  silc_fsm_thread_init(&conn->internal->packet_thread, &conn->internal->fsm,
+                      conn, silc_client_fsm_destructor, NULL, FALSE);
+  silc_fsm_start_sync(&conn->internal->packet_thread,
+                     silc_client_connection_st_packet);
+
+  /* Schedule any events set in initialization */
+  if (conn->internal->connect)
+    SILC_FSM_SEMA_POST(&conn->internal->wait_event);
+  if (conn->internal->key_exchange)
+    SILC_FSM_SEMA_POST(&conn->internal->wait_event);
+
+  /* Wait until this thread is terminated */
+  return SILC_FSM_WAIT;
 }
 
-/* Initializes the client. This makes all the necessary steps to make
-   the client ready to be run. One must call silc_client_run to run the
-   client. Returns FALSE if error occured, TRUE otherwise. */
+/* Connection machine main state. */
 
-SilcBool silc_client_init(SilcClient client)
+SILC_FSM_STATE(silc_client_connection_st_run)
 {
-  SILC_LOG_DEBUG(("Initializing client"));
+  SilcClientConnection conn = fsm_context;
 
-  assert(client);
-  assert(client->username);
-  assert(client->hostname);
-  assert(client->realname);
+  /* Wait for events */
+  SILC_FSM_SEMA_WAIT(&conn->internal->wait_event);
 
-  /* Validate essential strings */
-  if (client->nickname)
-    if (!silc_identifier_verify(client->nickname, strlen(client->nickname),
-                               SILC_STRING_UTF8, 128)) {
-      SILC_LOG_ERROR(("Malformed nickname '%s'", client->nickname));
-      return FALSE;
-    }
-  if (!silc_identifier_verify(client->username, strlen(client->username),
-                             SILC_STRING_UTF8, 128)) {
-    SILC_LOG_ERROR(("Malformed username '%s'", client->username));
-    return FALSE;
+  /* Process events */
+
+  if (conn->internal->connect) {
+    SILC_LOG_DEBUG(("Event: connect"));
+    conn->internal->connect = FALSE;
+
+    /** Connect remote host */
+    silc_fsm_thread_init(&conn->internal->event_thread, &conn->internal->fsm,
+                        conn, NULL, NULL, FALSE);
+    silc_fsm_start_sync(&conn->internal->event_thread, silc_client_st_connect);
+    return SILC_FSM_CONTINUE;
   }
-  if (!silc_identifier_verify(client->hostname, strlen(client->hostname),
-                             SILC_STRING_UTF8, 256)) {
-    SILC_LOG_ERROR(("Malformed hostname '%s'", client->hostname));
-    return FALSE;
+
+  if (conn->internal->key_exchange) {
+    SILC_LOG_DEBUG(("Event: key exchange"));
+    conn->internal->key_exchange = FALSE;
+
+    /** Start key exchange */
+    silc_fsm_thread_init(&conn->internal->event_thread, &conn->internal->fsm,
+                        conn, NULL, NULL, FALSE);
+    silc_fsm_start_sync(&conn->internal->event_thread,
+                       silc_client_st_connect_key_exchange);
+    return SILC_FSM_CONTINUE;
   }
-  if (!silc_utf8_valid(client->realname, strlen(client->realname))) {
-    SILC_LOG_ERROR(("Malformed realname '%s'", client->realname));
-    return FALSE;
+
+  if (conn->internal->disconnected) {
+    SILC_LOG_DEBUG(("Event: disconnected"));
+    conn->internal->disconnected = FALSE;
+
+    return SILC_FSM_CONTINUE;
   }
 
-  if (!client->internal->params->dont_register_crypto_library) {
-    /* Initialize the crypto library.  If application has done this already
-       this has no effect.  Also, we will not be overriding something
-       application might have registered earlier. */
-    silc_cipher_register_default();
-    silc_pkcs_register_default();
-    silc_hash_register_default();
-    silc_hmac_register_default();
+  /* NOT REACHED */
+#if defined(SILC_DEBUG)
+  assert(FALSE);
+#endif /* SILC_DEBUG */
+  return SILC_FSM_CONTINUE;
+}
+
+/* Connection's packet processor main state.  Packet processor thread waits
+   here for a new packet and processes received packets. */
+
+SILC_FSM_STATE(silc_client_connection_st_packet)
+{
+  SilcClientConnection conn = fsm_context;
+  SilcClient client = conn->client;
+  SilcPacket packet = state_context;
+
+  /* Wait for packet to arrive */
+  if (!conn->internal->new_packet) {
+    SILC_LOG_DEBUG(("Wait for packet"));
+    return SILC_FSM_WAIT;
   }
+  conn->internal->new_packet = FALSE;
 
-  /* Initialize hash functions for client to use */
-  silc_hash_alloc("md5", &client->md5hash);
-  silc_hash_alloc("sha1", &client->sha1hash);
+  SILC_LOG_DEBUG(("Parsing %s packet", silc_get_packet_name(packet->type)));
 
-  /* Initialize none cipher */
-  silc_cipher_alloc("none", &client->internal->none_cipher);
+  switch (packet->type) {
 
-  /* Initialize random number generator */
-  client->rng = silc_rng_alloc();
-  silc_rng_init(client->rng);
-  silc_rng_global_init(client->rng);
+  case SILC_PACKET_PRIVATE_MESSAGE:
+    /** Private message */
+    silc_fsm_next(fsm, silc_client_private_message);
+    break;
 
-  /* Register protocols */
-  silc_client_protocols_register();
+  case SILC_PACKET_CHANNEL_MESSAGE:
+    /* Channel message */
+    //    silc_client_channel_message(client, conn, packet);
+    break;
 
-  /* Initialize the scheduler */
-  client->schedule =
-    silc_schedule_init(client->internal->params->task_max ?
-                      client->internal->params->task_max : 200, client);
-  if (!client->schedule)
-    return FALSE;
+  case SILC_PACKET_FTP:
+    /* File transfer packet */
+    //    silc_client_ftp(client, conn, packet);
+    break;
 
-  /* Register commands */
-  silc_client_commands_register(client);
+  case SILC_PACKET_CHANNEL_KEY:
+    /* Received channel key */
+    //    silc_client_channel_key(client, conn, packet);
+    break;
 
-  return TRUE;
-}
+  case SILC_PACKET_COMMAND_REPLY:
+    /** Command reply */
+    silc_fsm_next(fsm, silc_client_command_reply);
+    break;
 
-/* Stops the client. This is called to stop the client and thus to stop
-   the program. */
+  case SILC_PACKET_NOTIFY:
+    /* Notify */
+    //    silc_client_notify(client, conn, packet);
+    break;
 
-void silc_client_stop(SilcClient client)
-{
-  SILC_LOG_DEBUG(("Stopping client"));
+  case SILC_PACKET_PRIVATE_MESSAGE_KEY:
+    /* Private message key indicator */
+    //    silc_client_private_message_key(client, conn, packet);
+    break;
 
-  silc_schedule_stop(client->schedule);
-  silc_schedule_uninit(client->schedule);
+  case SILC_PACKET_DISCONNECT:
+    /* Server disconnects */
+    //    silc_client_disconnect(client, conn, packet);
+    break;
 
-  silc_client_protocols_unregister();
-  silc_client_commands_unregister(client);
+  case SILC_PACKET_ERROR:
+    /* Error by server */
+    //    silc_client_error(client, conn, packet);
+    break;
 
-  SILC_LOG_DEBUG(("Client stopped"));
+  case SILC_PACKET_KEY_AGREEMENT:
+    /* Key agreement */
+    //    silc_client_key_agreement(client, conn, packet);
+    break;
+
+  case SILC_PACKET_COMMAND:
+    /** Command packet */
+    silc_fsm_next(fsm, silc_client_command);
+    break;
+
+  case SILC_PACKET_NEW_ID:
+    /** New ID */
+    silc_fsm_next(fsm, silc_client_new_id);
+
+  case SILC_PACKET_CONNECTION_AUTH_REQUEST:
+    /* Reply to connection authentication request to resolve authentication
+       method from server. */
+    //    silc_client_connection_auth_request(client, conn, packet);
+    break;
+
+  default:
+    silc_packet_free(packet);
+    break;
+  }
+
+  return SILC_FSM_CONTINUE;
 }
 
-/* Runs the client. This starts the scheduler from the utility library.
-   When this functions returns the execution of the appliation is over. */
 
-void silc_client_run(SilcClient client)
+/*************************** Main client machine ****************************/
+
+/* The client's main state where we wait for various events */
+
+SILC_FSM_STATE(silc_client_st_run)
 {
-  SILC_LOG_DEBUG(("Running client"));
+  SilcClient client = fsm_context;
 
-  assert(client);
-  assert(client->pkcs);
-  assert(client->public_key);
-  assert(client->private_key);
+  /* Wait for events */
+  SILC_FSM_SEMA_WAIT(&client->internal->wait_event);
 
-  /* Start the scheduler, the heart of the SILC client. When this returns
-     the program will be terminated. */
-  silc_schedule(client->schedule);
+  /* Process events */
+
+  if (client->internal->run_callback && client->internal->ops->running) {
+    /* Call running callbcak back to application */
+    client->internal->run_callback = FALSE;
+    client->internal->ops->running(client, client->application);
+    return SILC_FSM_CONTINUE;
+  }
+
+  /* NOT REACHED */
+#if defined(SILC_DEBUG)
+  assert(FALSE);
+#endif /* SILC_DEBUG */
+  return SILC_FSM_CONTINUE;
 }
 
-/* Runs the client and returns immeadiately. This function is used when
-   the SILC Client object indicated by the `client' is run under some
-   other scheduler, or event loop or main loop.  On GUI applications,
-   for example this may be desired to use to run the client under the
-   GUI application's main loop.  Typically the GUI application would
-   register an idle task that calls this function multiple times in
-   a second to quickly process the SILC specific data. */
 
-void silc_client_run_one(SilcClient client)
+/**************************** Packet Processing *****************************/
+
+/* Received new ID from server during registering to SILC network */
+
+SILC_FSM_STATE(silc_client_new_id)
 {
-  /* Run the scheduler once. */
-  silc_schedule_one(client->schedule, 0);
+  SilcClientConnection conn = fsm_context;
+  SilcClient client = conn->client;
+  SilcPacket packet = state_context;
+  SilcID id;
+
+  if (conn->local_id)
+    goto out;
+
+  SILC_LOG_DEBUG(("New ID received from server"));
+
+  if (!silc_id_payload_parse_id(silc_buffer_data(&packet->buffer),
+                               silc_buffer_len(&packet->buffer), &id))
+    goto out;
+
+  /* Create local client entry */
+  conn->local_entry = silc_client_add_client(client, conn, (client->nickname ?
+                                                           client->nickname :
+                                                           client->username),
+                                            client->username,
+                                            client->realname,
+                                            &id.u.client_id, 0);
+  if (!conn->local_entry)
+    goto out;
+
+  /* Save the ID */
+  conn->local_id = &conn->local_entry->id;
+  conn->local_idp = silc_buffer_copy(&packet->buffer);
+
+  /* Save cache entry */
+  silc_idcache_find_by_id_one(conn->internal->client_cache, conn->local_id,
+                             &conn->internal->local_entry);
+
+  /* Save remote ID */
+  if (packet->src_id_len) {
+    conn->remote_idp = silc_id_payload_encode_data(packet->src_id,
+                                                  packet->src_id_len,
+                                                  packet->src_id_type);
+    if (!conn->remote_idp)
+      goto out;
+    silc_id_payload_parse_id(silc_buffer_data(conn->remote_idp),
+                            silc_buffer_len(conn->remote_idp),
+                            &conn->remote_id);
+  }
+
+  /* Signal connection that new ID was received so it can continue
+     with the registering. */
+  if (conn->internal->registering)
+    silc_fsm_continue_sync(&conn->internal->event_thread);
+
+ out:
+  /** Packet processed */
+  silc_packet_free(packet);
+  silc_fsm_next(fsm, silc_client_connection_st_packet);
+  return SILC_FSM_CONTINUE;
 }
 
+
+/******************************* Public API *********************************/
+
 /* Allocates and adds new connection to the client. This adds the allocated
    connection to the connection table and returns a pointer to it. A client
    can have multiple connections to multiple servers. Every connection must
@@ -237,30 +383,49 @@ void silc_client_run_one(SilcClient client)
 
 SilcClientConnection
 silc_client_add_connection(SilcClient client,
-                           SilcClientConnectionParams *params,
-                           char *hostname, int port, void *context)
+                          SilcConnectionType conn_type,
+                          SilcClientConnectionParams *params,
+                          SilcPublicKey public_key,
+                          SilcPrivateKey private_key,
+                          char *remote_host, int port,
+                          SilcClientConnectCallback callback,
+                          void *context)
 {
   SilcClientConnection conn;
-  int i;
+  SilcFSMThread thread;
 
-  SILC_LOG_DEBUG(("Adding new connection to %s:%d", hostname, port));
+  if (!callback)
+    return NULL;
+
+  SILC_LOG_DEBUG(("Adding new connection to %s:%d", remote_host, port));
 
   conn = silc_calloc(1, sizeof(*conn));
+  if (!conn)
+    return NULL;
   conn->internal = silc_calloc(1, sizeof(*conn->internal));
+  if (!conn->internal) {
+    silc_free(conn);
+    return NULL;
+  }
 
-  /* Initialize ID caches */
   conn->client = client;
-  conn->remote_host = strdup(hostname);
-  conn->remote_port = port;
+  conn->public_key = public_key;
+  conn->private_key = private_key;
+  conn->remote_host = strdup(remote_host);
+  conn->remote_port = port ? port : 706;
+  conn->type = conn_type;
+  conn->callback = callback;
   conn->context = context;
   conn->internal->client_cache =
-    silc_idcache_alloc(0, SILC_ID_CLIENT, NULL, NULL, FALSE, TRUE);
+    silc_idcache_alloc(0, SILC_ID_CLIENT, NULL, NULL);
   conn->internal->channel_cache = silc_idcache_alloc(0, SILC_ID_CHANNEL, NULL,
-                                                    NULL, FALSE, TRUE);
+                                                    NULL);
   conn->internal->server_cache = silc_idcache_alloc(0, SILC_ID_SERVER, NULL,
-                                                   NULL, FALSE, TRUE);
-  conn->internal->pending_commands = silc_dlist_init();
+                                                   NULL);
   conn->internal->ftp_sessions = silc_dlist_init();
+  conn->internal->verbose = TRUE;
+  silc_list_init(conn->internal->pending_commands,
+                struct SilcClientCommandContextStruct, next);
 
   if (params) {
     if (params->detach_data)
@@ -270,18 +435,19 @@ silc_client_add_connection(SilcClient client,
     conn->internal->params.detach_data_len = params->detach_data_len;
   }
 
-  /* Add the connection to connections table */
-  for (i = 0; i < client->internal->conns_count; i++)
-    if (client->internal->conns && !client->internal->conns[i]) {
-      client->internal->conns[i] = conn;
-      return conn;
-    }
+  /* Add the connection to connections list */
+  //  silc_dlist_add(client->internal->conns, conn);
 
-  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++;
+  /* Run the connection state machine.  If threads are in use the machine
+     is always run in a real thread. */
+  thread = silc_fsm_thread_alloc(&client->internal->fsm, conn,
+                                silc_client_fsm_destructor, NULL,
+                                client->internal->params->threads);
+  if (!thread) {
+    silc_client_del_connection(client, conn);
+    return NULL;
+  }
+  silc_fsm_start_sync(thread, silc_client_connection_st_start);
 
   return conn;
 }
@@ -290,266 +456,125 @@ silc_client_add_connection(SilcClient client,
 
 void silc_client_del_connection(SilcClient client, SilcClientConnection conn)
 {
-  int i;
-
-  for (i = 0; i < client->internal->conns_count; i++)
-    if (client->internal->conns[i] == conn) {
-      /* Free all cache entries */
-      SilcIDCacheList list;
-      SilcIDCacheEntry entry;
-      SilcClientCommandPending *r;
-      SilcBool ret;
-
-      if (silc_idcache_get_all(conn->internal->client_cache, &list)) {
-       ret = silc_idcache_list_first(list, &entry);
-       while (ret) {
-         silc_client_del_client(client, conn, entry->context);
-         ret = silc_idcache_list_next(list, &entry);
-       }
-       silc_idcache_list_free(list);
-      }
-
-      if (silc_idcache_get_all(conn->internal->channel_cache, &list)) {
-       ret = silc_idcache_list_first(list, &entry);
-       while (ret) {
-         silc_client_del_channel(client, conn, entry->context);
-         ret = silc_idcache_list_next(list, &entry);
-       }
-       silc_idcache_list_free(list);
-      }
-
-      if (silc_idcache_get_all(conn->internal->server_cache, &list)) {
-       ret = silc_idcache_list_first(list, &entry);
-       while (ret) {
-         silc_client_del_server(client, conn, entry->context);
-         ret = silc_idcache_list_next(list, &entry);
-       }
-       silc_idcache_list_free(list);
+#if 0
+  SilcClientConnection c;
+  SilcIDCacheList list;
+  SilcIDCacheEntry entry;
+  SilcClientCommandPending *r;
+  SilcBool ret;
+
+  silc_dlist_start(client->internal->conns);
+  while ((c = silc_dlist_get(client->internal->conns)) != SILC_LIST_END) {
+    if (c != conn)
+      continue;
+
+    /* Free all cache entries */
+    if (silc_idcache_get_all(conn->internal->client_cache, &list)) {
+      ret = silc_idcache_list_first(list, &entry);
+      while (ret) {
+       silc_client_del_client(client, conn, entry->context);
+       ret = silc_idcache_list_next(list, &entry);
       }
+      silc_idcache_list_free(list);
+    }
 
-      /* Clear ID caches */
-      if (conn->internal->client_cache)
-       silc_idcache_free(conn->internal->client_cache);
-      if (conn->internal->channel_cache)
-       silc_idcache_free(conn->internal->channel_cache);
-      if (conn->internal->server_cache)
-       silc_idcache_free(conn->internal->server_cache);
-
-      /* Free data (my ID is freed in above silc_client_del_client).
-        conn->nickname is freed when freeing the local_entry->nickname. */
-      silc_free(conn->remote_host);
-      silc_free(conn->local_id_data);
-      if (conn->internal->send_key)
-       silc_cipher_free(conn->internal->send_key);
-      if (conn->internal->receive_key)
-       silc_cipher_free(conn->internal->receive_key);
-      if (conn->internal->hmac_send)
-       silc_hmac_free(conn->internal->hmac_send);
-      if (conn->internal->hmac_receive)
-       silc_hmac_free(conn->internal->hmac_receive);
-      silc_free(conn->internal->rekey);
-
-      if (conn->internal->active_session) {
-       if (conn->sock)
-         conn->sock->user_data = NULL;
-       silc_client_ftp_session_free(conn->internal->active_session);
-       conn->internal->active_session = NULL;
+    if (silc_idcache_get_all(conn->internal->channel_cache, &list)) {
+      ret = silc_idcache_list_first(list, &entry);
+      while (ret) {
+       silc_client_del_channel(client, conn, entry->context);
+       ret = silc_idcache_list_next(list, &entry);
       }
+      silc_idcache_list_free(list);
+    }
 
-      silc_client_ftp_free_sessions(client, conn);
-
-      if (conn->internal->pending_commands) {
-       silc_dlist_start(conn->internal->pending_commands);
-       while ((r = silc_dlist_get(conn->internal->pending_commands))
-              != SILC_LIST_END)
-         silc_dlist_del(conn->internal->pending_commands, r);
-       silc_dlist_uninit(conn->internal->pending_commands);
+    if (silc_idcache_get_all(conn->internal->server_cache, &list)) {
+      ret = silc_idcache_list_first(list, &entry);
+      while (ret) {
+       silc_client_del_server(client, conn, entry->context);
+       ret = silc_idcache_list_next(list, &entry);
       }
-
-      silc_free(conn->internal);
-      memset(conn, 0, sizeof(*conn));
-      silc_free(conn);
-
-      client->internal->conns[i] = NULL;
+      silc_idcache_list_free(list);
     }
-}
-
-/* Adds listener socket to the listener sockets table. This function is
-   used to add socket objects that are listeners to the client.  This should
-   not be used to add other connection objects. */
-
-void silc_client_add_socket(SilcClient client, SilcSocketConnection sock)
-{
-  int i;
 
-  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;
-  }
+    /* Clear ID caches */
+    if (conn->internal->client_cache)
+      silc_idcache_free(conn->internal->client_cache);
+    if (conn->internal->channel_cache)
+      silc_idcache_free(conn->internal->channel_cache);
+    if (conn->internal->server_cache)
+      silc_idcache_free(conn->internal->server_cache);
 
-  for (i = 0; i < client->internal->sockets_count; i++) {
-    if (client->internal->sockets[i] == NULL) {
-      client->internal->sockets[i] = silc_socket_dup(sock);
-      return;
+    /* Free data (my ID is freed in above silc_client_del_client).
+       conn->nickname is freed when freeing the local_entry->nickname. */
+    silc_free(conn->remote_host);
+    silc_free(conn->local_id_data);
+    if (conn->internal->send_key)
+      silc_cipher_free(conn->internal->send_key);
+    if (conn->internal->receive_key)
+      silc_cipher_free(conn->internal->receive_key);
+    if (conn->internal->hmac_send)
+      silc_hmac_free(conn->internal->hmac_send);
+    if (conn->internal->hmac_receive)
+      silc_hmac_free(conn->internal->hmac_receive);
+    silc_free(conn->internal->rekey);
+
+    if (conn->internal->active_session) {
+      if (conn->sock)
+       conn->sock->user_data = NULL;
+      silc_client_ftp_session_free(conn->internal->active_session);
+      conn->internal->active_session = NULL;
     }
-  }
-
-  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. */
+    silc_client_ftp_free_sessions(client, conn);
 
-void silc_client_del_socket(SilcClient client, SilcSocketConnection sock)
-{
-  int i;
+    if (conn->internal->pending_commands) {
+      silc_dlist_start(conn->internal->pending_commands);
+      while ((r = silc_dlist_get(conn->internal->pending_commands))
+            != SILC_LIST_END)
+       silc_dlist_del(conn->internal->pending_commands, r);
+      silc_dlist_uninit(conn->internal->pending_commands);
+    }
 
-  if (!client->internal->sockets)
-    return;
+    silc_free(conn->internal);
+    memset(conn, 0, sizeof(*conn));
+    silc_free(conn);
 
-  for (i = 0; i < client->internal->sockets_count; i++) {
-    if (client->internal->sockets[i] == sock) {
-      silc_socket_free(sock);
-      client->internal->sockets[i] = NULL;
-      return;
-    }
+    silc_dlist_del(client->internal->conns, conn);
   }
-}
-
-static int
-silc_client_connect_to_server_internal(SilcClientInternalConnectContext *ctx)
-{
-  int sock;
-
-  /* XXX In the future we should give up this non-blocking connect all
-     together and use threads instead. */
-  /* Create connection to server asynchronously */
-  sock = silc_net_create_connection_async(NULL, ctx->port, ctx->host);
-  if (sock < 0)
-    return -1;
-
-  /* Register task that will receive the async connect and will
-     read the result. */
-  ctx->task = silc_schedule_task_add(ctx->client->schedule, sock,
-                                    silc_client_connect_to_server_start,
-                                    (void *)ctx, 0, 0,
-                                    SILC_TASK_FD,
-                                    SILC_TASK_PRI_NORMAL);
-  silc_schedule_set_listen_fd(ctx->client->schedule, sock, SILC_TASK_WRITE,
-                             FALSE);
-
-  ctx->sock = sock;
-
-  return sock;
+#endif /* 0 */
 }
 
 /* Connects to remote server. This is the main routine used to connect
-   to SILC server. Returns -1 on error and the created socket otherwise.
-   The `context' is user context that is saved into the SilcClientConnection
-   that is created after the connection is created. Note that application
-   may handle the connecting process outside the library. If this is the
-   case then this function is not used at all. When the connecting is
-   done the `connect' client operation is called. */
-
-int silc_client_connect_to_server(SilcClient client,
-                                 SilcClientConnectionParams *params,
-                                 int port, char *host, void *context)
+   to remote SILC server. Returns FALSE on error. */
+
+void silc_client_connect_to_server(SilcClient client,
+                                  SilcClientConnectionParams *params,
+                                  SilcPublicKey public_key,
+                                  SilcPrivateKey private_key,
+                                  char *remote_host, int port,
+                                  SilcClientConnectCallback callback,
+                                  void *context)
 {
-  SilcClientInternalConnectContext *ctx;
   SilcClientConnection conn;
-  int sock;
-
-  SILC_LOG_DEBUG(("Connecting to port %d of server %s",
-                 port, host));
-
-  conn = silc_client_add_connection(client, params, host, port, context);
-
-  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. */
-  ctx = silc_calloc(1, sizeof(*ctx));
-  ctx->client = client;
-  ctx->conn = conn;
-  ctx->host = strdup(host);
-  ctx->port = port ? port : 706;
-  ctx->tries = 0;
-
-  /* Do the actual connecting process */
-  sock = silc_client_connect_to_server_internal(ctx);
-  if (sock == -1)
-    silc_client_del_connection(client, conn);
-  return sock;
-}
-
-/* Socket hostname and IP lookup callback that is called before actually
-   starting the key exchange.  The lookup is called from the function
-   silc_client_start_key_exchange. */
-
-static void silc_client_start_key_exchange_cb(SilcSocketConnection sock,
-                                             void *context)
-{
-  SilcClientConnection conn = (SilcClientConnection)context;
-  SilcClient client = conn->client;
-  SilcProtocol protocol;
-  SilcClientKEInternalContext *proto_ctx;
-
-  SILC_LOG_DEBUG(("Start"));
 
-  if (conn->sock->hostname) {
-    silc_free(conn->remote_host);
-    conn->remote_host = strdup(conn->sock->hostname);
-  } else {
-    conn->sock->hostname = strdup(conn->remote_host);
-  }
-  if (!conn->sock->ip)
-    conn->sock->ip = strdup(conn->sock->hostname);
-  conn->sock->port = conn->remote_port;
+  if (!client || !remote_host)
+    return;
 
-  /* Allocate internal Key Exchange context. This is sent to the
-     protocol as context. */
-  proto_ctx = silc_calloc(1, sizeof(*proto_ctx));
-  proto_ctx->client = (void *)client;
-  proto_ctx->sock = silc_socket_dup(conn->sock);
-  proto_ctx->rng = client->rng;
-  proto_ctx->responder = FALSE;
-  proto_ctx->send_packet = silc_client_protocol_ke_send_packet;
-  proto_ctx->verify = silc_client_protocol_ke_verify_key;
-
-  /* Perform key exchange protocol. silc_client_connect_to_server_final
-     will be called after the protocol is finished. */
-  silc_protocol_alloc(SILC_PROTOCOL_CLIENT_KEY_EXCHANGE,
-                     &protocol, (void *)proto_ctx,
-                     silc_client_connect_to_server_second);
-  if (!protocol) {
-    client->internal->ops->say(client, conn, SILC_CLIENT_MESSAGE_ERROR,
-                              "Error: Could not start key exchange protocol");
-    silc_net_close_connection(conn->sock->sock);
-    client->internal->ops->connected(client, conn, SILC_CLIENT_CONN_ERROR);
+  /* Add new connection */
+  conn = silc_client_add_connection(client, SILC_CONN_SERVER, params,
+                                   public_key, private_key, remote_host,
+                                   port, callback, context);
+  if (!conn) {
+    callback(client, NULL, SILC_CLIENT_CONN_ERROR, context);
     return;
   }
-  conn->sock->protocol = protocol;
 
-  /* Register the connection for network input and output. This sets
-     that scheduler will listen for incoming packets for this connection
-     and sets that outgoing packets may be sent to this connection as well.
-     However, this doesn't set the scheduler for outgoing traffic, it will
-     be set separately by calling SILC_CLIENT_SET_CONNECTION_FOR_OUTPUT,
-     later when outgoing data is available. */
-  context = (void *)client;
-  SILC_CLIENT_REGISTER_CONNECTION_FOR_IO(conn->sock->sock);
+  client->internal->ops->say(client, conn, SILC_CLIENT_MESSAGE_AUDIT,
+                            "Connecting to port %d of server %s",
+                            port, remote_host);
 
-  /* Execute the protocol */
-  silc_protocol_execute(protocol, client->schedule, 0, 0);
+  /* Signal connection machine to start connecting */
+  conn->internal->connect = TRUE;
 }
 
 /* Start SILC Key Exchange (SKE) protocol to negotiate shared secret
@@ -561,188 +586,22 @@ static void silc_client_start_key_exchange_cb(SilcSocketConnection sock,
 
 void silc_client_start_key_exchange(SilcClient client,
                                    SilcClientConnection conn,
-                                   int fd)
+                                   SilcStream stream)
 {
-  assert(client->pkcs);
+#if 0
+  assert(conn && stream);
   assert(client->public_key);
   assert(client->private_key);
 
-  /* Allocate new socket connection object */
-  silc_socket_alloc(fd, SILC_SOCKET_TYPE_SERVER, (void *)conn, &conn->sock);
-
-  /* Sometimes when doing quick reconnects the new socket may be same as
-     the old one and there might be pending stuff for the old socket.
-     If new one is same then those pending sutff might cause problems.
-     Make sure they do not do that. */
-  silc_schedule_task_del_by_fd(client->schedule, fd);
-
   conn->nickname = (client->nickname ? strdup(client->nickname) :
                    strdup(client->username));
+#endif /* 0 */
 
-  /* Resolve the remote hostname and IP address for our socket connection */
-  silc_socket_host_lookup(conn->sock, FALSE, silc_client_start_key_exchange_cb,
-                         conn, client->schedule);
-}
-
-/* Callback called when error has occurred during connecting (KE) to
-   the server.  The `connect' client operation will be called. */
-
-SILC_TASK_CALLBACK(silc_client_connect_failure)
-{
-  SilcClientKEInternalContext *ctx =
-    (SilcClientKEInternalContext *)context;
-  SilcClient client = (SilcClient)ctx->client;
-
-  client->internal->ops->connected(client, ctx->sock->user_data,
-                                  SILC_CLIENT_CONN_ERROR_KE);
-  if (ctx->packet)
-    silc_packet_context_free(ctx->packet);
-  silc_free(ctx);
-}
-
-/* Callback called when error has occurred during connecting (auth) to
-   the server.  The `connect' client operation will be called. */
-
-SILC_TASK_CALLBACK(silc_client_connect_failure_auth)
-{
-  SilcClientConnAuthInternalContext *ctx =
-    (SilcClientConnAuthInternalContext *)context;
-  SilcClient client = (SilcClient)ctx->client;
-
-  client->internal->ops->connected(client, ctx->sock->user_data, ctx->status);
-  silc_free(ctx);
-}
-
-/* Start of the connection to the remote server. This is called after
-   succesful TCP/IP connection has been established to the remote host. */
-
-SILC_TASK_CALLBACK(silc_client_connect_to_server_start)
-{
-  SilcClientInternalConnectContext *ctx =
-    (SilcClientInternalConnectContext *)context;
-  SilcClient client = ctx->client;
-  SilcClientConnection conn = ctx->conn;
-  int opt, opt_len = sizeof(opt);
-
-  SILC_LOG_DEBUG(("Start"));
-
-  /* Check the socket status as it might be in error */
-  silc_net_get_socket_opt(fd, SOL_SOCKET, SO_ERROR, &opt, &opt_len);
-  if (opt != 0) {
-    if (ctx->tries < 2) {
-      /* Connection failed but lets try again */
-      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);
-      silc_net_close_connection(fd);
-      silc_schedule_task_del(client->schedule, ctx->task);
-
-      /* Try again */
-      silc_client_connect_to_server_internal(ctx);
-      ctx->tries++;
-    } else {
-      /* Connection failed and we won't try anymore */
-      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->internal->ops->connected(client, conn,
-                                      SILC_CLIENT_CONN_ERROR_TIMEOUT);
-    }
-    return;
-  }
-
-  silc_schedule_unset_listen_fd(client->schedule, fd);
-  silc_schedule_task_del(client->schedule, ctx->task);
-  silc_free(ctx);
-
-  silc_client_start_key_exchange(client, conn, fd);
-}
-
-/* Second part of the connecting to the server. This executed
-   authentication protocol. */
-
-SILC_TASK_CALLBACK(silc_client_connect_to_server_second)
-{
-  SilcProtocol protocol = (SilcProtocol)context;
-  SilcClientKEInternalContext *ctx =
-    (SilcClientKEInternalContext *)protocol->context;
-  SilcClient client = (SilcClient)ctx->client;
-  SilcSocketConnection sock = NULL;
-  SilcClientConnAuthInternalContext *proto_ctx;
-
-  SILC_LOG_DEBUG(("Start"));
-
-  if (protocol->state == SILC_PROTOCOL_STATE_ERROR ||
-      protocol->state == SILC_PROTOCOL_STATE_FAILURE) {
-    /* Error occured during protocol */
-    SILC_LOG_DEBUG(("Error during KE protocol"));
-    silc_protocol_free(protocol);
-    silc_ske_free_key_material(ctx->keymat);
-    if (ctx->ske)
-      silc_ske_free(ctx->ske);
-    if (ctx->dest_id)
-      silc_free(ctx->dest_id);
-    ctx->sock->protocol = NULL;
-    silc_socket_free(ctx->sock);
-
-    /* Notify application of failure */
-    silc_schedule_task_add(client->schedule, ctx->sock->sock,
-                          silc_client_connect_failure, ctx,
-                          0, 1, SILC_TASK_TIMEOUT, SILC_TASK_PRI_NORMAL);
-    return;
-  }
-
-  /* We now have the key material as the result of the key exchange
-     protocol. Take the key material into use. Free the raw key material
-     as soon as we've set them into use. */
-  silc_client_protocol_ke_set_keys(ctx->ske, ctx->sock, ctx->keymat,
-                                  ctx->ske->prop->cipher,
-                                  ctx->ske->prop->pkcs,
-                                  ctx->ske->prop->hash,
-                                  ctx->ske->prop->hmac,
-                                  ctx->ske->prop->group,
-                                  ctx->responder);
-  silc_ske_free_key_material(ctx->keymat);
-
-  /* Allocate internal context for the authentication protocol. This
-     is sent as context for the protocol. */
-  proto_ctx = silc_calloc(1, sizeof(*proto_ctx));
-  proto_ctx->client = (void *)client;
-  proto_ctx->sock = sock = ctx->sock;
-  proto_ctx->ske = ctx->ske;   /* Save SKE object from previous protocol */
-  proto_ctx->dest_id_type = ctx->dest_id_type;
-  proto_ctx->dest_id = ctx->dest_id;
-
-  /* Free old protocol as it is finished now */
-  silc_protocol_free(protocol);
-  if (ctx->packet)
-    silc_packet_context_free(ctx->packet);
-  ctx->packet = NULL;
-  silc_free(ctx);
-  sock->protocol = NULL;
+  /* Start */
 
-  /* Resolve the authentication method to be used in this connection. The
-     completion callback is called after the application has resolved
-     the authentication method. */
-  client->internal->ops->get_auth_method(client, sock->user_data,
-                                        sock->hostname,
-                                        sock->port,
-                                        silc_client_resolve_auth_method,
-                                        proto_ctx);
 }
 
+#if 0
 /* Authentication method resolving callback. Application calls this function
    after we've called the client->internal->ops->get_auth_method
    client operation to resolve the authentication method. We will continue
@@ -922,51 +781,13 @@ SILC_TASK_CALLBACK(silc_client_connect_to_server_final)
                         0, 1, SILC_TASK_TIMEOUT, SILC_TASK_PRI_NORMAL);
 }
 
-/* Internal routine that sends packet or marks packet to be sent. This
-   is used directly only in special cases. Normal cases should use
-   silc_server_packet_send. Returns < 0 on error. */
-
-int silc_client_packet_send_real(SilcClient client,
-                                SilcSocketConnection sock,
-                                SilcBool force_send)
-{
-  int ret;
-
-  /* If rekey protocol is active we must assure that all packets are
-     sent through packet queue. */
-  if (SILC_CLIENT_IS_REKEY(sock))
-    force_send = FALSE;
-
-  /* If outbound data is already pending do not force send */
-  if (SILC_IS_OUTBUF_PENDING(sock))
-    force_send = FALSE;
-
-  /* Send the packet */
-  ret = silc_packet_send(sock, force_send);
-  if (ret != -2)
-    return ret;
-
-  /* Mark that there is some outgoing data available for this connection.
-     This call sets the connection both for input and output (the input
-     is set always and this call keeps the input setting, actually).
-     Actual data sending is performed by silc_client_packet_process. */
-  SILC_CLIENT_SET_CONNECTION_FOR_OUTPUT(client->schedule, sock->sock);
-
-  /* Mark to socket that data is pending in outgoing buffer. This flag
-     is needed if new data is added to the buffer before the earlier
-     put data is sent to the network. */
-  SILC_SET_OUTBUF_PENDING(sock);
-
-  return 0;
-}
-
 /* Packet processing callback. This is used to send and receive packets
    from network. This is generic task. */
 
 SILC_TASK_CALLBACK_GLOBAL(silc_client_packet_process)
 {
   SilcClient client = (SilcClient)context;
-  SilcSocketConnection sock = NULL;
+  SilcClientConnection conn = NULL;
   SilcClientConnection conn;
   int ret;
 
@@ -1043,507 +864,72 @@ SILC_TASK_CALLBACK_GLOBAL(silc_client_packet_process)
     else
       silc_packet_receive_process(sock, FALSE, NULL, NULL, 0,
                                  silc_client_packet_parse, client);
-  }
-}
-
-/* Parser callback called by silc_packet_receive_process. Thie merely
-   registers timeout that will handle the actual parsing when appropriate. */
-
-static SilcBool silc_client_packet_parse(SilcPacketParserContext *parser_context,
-                                    void *context)
-{
-  SilcClient client = (SilcClient)context;
-  SilcSocketConnection sock = parser_context->sock;
-  SilcClientConnection conn = (SilcClientConnection)sock->user_data;
-  SilcPacketContext *packet = parser_context->packet;
-  SilcPacketType ret;
-
-  if (conn && conn->internal->hmac_receive && conn->sock == sock)
-    conn->internal->psn_receive = parser_context->packet->sequence + 1;
-
-  /* Parse the packet immediately */
-  if (parser_context->normal)
-    ret = silc_packet_parse(packet, conn->internal->receive_key);
-  else
-    ret = silc_packet_parse_special(packet, conn->internal->receive_key);
-
-  if (ret == SILC_PACKET_NONE) {
-    silc_packet_context_free(packet);
-    silc_free(parser_context);
-    return FALSE;
-  }
-
-  /* If protocol for this connection is key exchange or rekey then we'll
-     process all packets synchronously, since there might be packets in
-     queue that we are not able to decrypt without first processing the
-     packets before them. */
-  if (sock->protocol && sock->protocol->protocol &&
-      (sock->protocol->protocol->type == SILC_PROTOCOL_CLIENT_KEY_EXCHANGE ||
-       sock->protocol->protocol->type == SILC_PROTOCOL_CLIENT_REKEY)) {
-
-    /* Parse the incoming packet type */
-    silc_client_packet_parse_type(client, sock, packet);
-
-    /* Reprocess the buffer since we'll return FALSE. This is because
-       the `conn->internal->receive_key' might have become valid by processing
-       the previous packet */
-    if (sock->type != SILC_SOCKET_TYPE_UNKNOWN)
-      silc_packet_receive_process(sock, FALSE, conn->internal->receive_key,
-                                 conn->internal->hmac_receive,
-                                 conn->internal->psn_receive,
-                                 silc_client_packet_parse, client);
-    else
-      silc_packet_receive_process(sock, FALSE, NULL, NULL, 0,
-                                 silc_client_packet_parse, client);
-
-    silc_packet_context_free(packet);
-    silc_free(parser_context);
-
-    return FALSE;
-  }
-
-  /* Parse the incoming packet type */
-  silc_client_packet_parse_type(client, sock, packet);
-  silc_packet_context_free(packet);
-  silc_free(parser_context);
-  return TRUE;
-}
-
-/* Parses the packet type and calls what ever routines the packet type
-   requires. This is done for all incoming packets. */
-
-void silc_client_packet_parse_type(SilcClient client,
-                                  SilcSocketConnection sock,
-                                  SilcPacketContext *packet)
-{
-  SilcBuffer buffer = packet->buffer;
-  SilcPacketType type = packet->type;
-
-  SILC_LOG_DEBUG(("Parsing %s packet", silc_get_packet_name(type)));
-
-  /* Parse the packet type */
-  switch(type) {
-
-  case SILC_PACKET_DISCONNECT:
-    silc_client_disconnected_by_server(client, sock, buffer);
-    break;
-
-  case SILC_PACKET_SUCCESS:
-    /*
-     * Success received for something. For now we can have only
-     * one protocol for connection executing at once hence this
-     * success message is for whatever protocol is executing currently.
-     */
-    if (sock->protocol)
-      silc_protocol_execute(sock->protocol, client->schedule, 0, 0);
-    break;
-
-  case SILC_PACKET_FAILURE:
-    /*
-     * Failure received for some protocol. Set the protocol state to
-     * error and call the protocol callback. This fill cause error on
-     * protocol and it will call the final callback.
-     */
-    silc_client_process_failure(client, sock, packet);
-    break;
-
-  case SILC_PACKET_REJECT:
-    break;
-
-  case SILC_PACKET_NOTIFY:
-    /*
-     * Received notify message
-     */
-    silc_client_notify_by_server(client, sock, packet);
-    break;
-
-  case SILC_PACKET_ERROR:
-    /*
-     * Received error message
-     */
-    silc_client_error_by_server(client, sock, buffer);
-    break;
-
-  case SILC_PACKET_CHANNEL_MESSAGE:
-    /*
-     * Received message to (from, actually) a channel
-     */
-    silc_client_channel_message(client, sock, packet);
-    break;
-
-  case SILC_PACKET_CHANNEL_KEY:
-    /*
-     * Received key for a channel. By receiving this key the client will be
-     * able to talk to the channel it has just joined. This can also be
-     * a new key for existing channel as keys expire peridiocally.
-     */
-    silc_client_receive_channel_key(client, sock, buffer);
-    break;
-
-  case SILC_PACKET_PRIVATE_MESSAGE:
-    /*
-     * Received private message
-     */
-    silc_client_private_message(client, sock, packet);
-    break;
-
-  case SILC_PACKET_PRIVATE_MESSAGE_KEY:
-    /*
-     * Received private message key indicator
-     */
-    silc_client_private_message_key(client, sock, packet);
-    break;
-
-  case SILC_PACKET_COMMAND:
-    /*
-     * Received command packet, a special case since normally client
-     * does not receive commands.
-     */
-    silc_client_command_process(client, sock, packet);
-    break;
-
-  case SILC_PACKET_COMMAND_REPLY:
-    /*
-     * Recived reply for a command
-     */
-    silc_client_command_reply_process(client, sock, packet);
-    break;
-
-  case SILC_PACKET_KEY_EXCHANGE:
-    if (sock->protocol && sock->protocol->protocol &&
-       sock->protocol->protocol->type == SILC_PROTOCOL_CLIENT_KEY_EXCHANGE) {
-      SilcClientKEInternalContext *proto_ctx =
-       (SilcClientKEInternalContext *)sock->protocol->context;
-
-      proto_ctx->packet = silc_packet_context_dup(packet);
-      proto_ctx->dest_id_type = packet->src_id_type;
-      proto_ctx->dest_id = silc_id_str2id(packet->src_id, packet->src_id_len,
-                                         packet->src_id_type);
-      if (!proto_ctx->dest_id)
-       break;
-
-      /* Let the protocol handle the packet */
-      silc_protocol_execute(sock->protocol, client->schedule, 0, 0);
-    } else {
-      SILC_LOG_ERROR(("Received Key Exchange packet but no key exchange "
-                     "protocol active, packet dropped."));
-    }
-    break;
-
-  case SILC_PACKET_KEY_EXCHANGE_1:
-    if (sock->protocol && sock->protocol->protocol &&
-       (sock->protocol->protocol->type == SILC_PROTOCOL_CLIENT_KEY_EXCHANGE ||
-        sock->protocol->protocol->type == SILC_PROTOCOL_CLIENT_REKEY)) {
-
-      if (sock->protocol->protocol->type == SILC_PROTOCOL_CLIENT_REKEY) {
-       SilcClientRekeyInternalContext *proto_ctx =
-         (SilcClientRekeyInternalContext *)sock->protocol->context;
-
-       if (proto_ctx->packet)
-         silc_packet_context_free(proto_ctx->packet);
-
-       proto_ctx->packet = silc_packet_context_dup(packet);
-
-       /* Let the protocol handle the packet */
-       silc_protocol_execute(sock->protocol, client->schedule, 0, 0);
-      } else {
-       SilcClientKEInternalContext *proto_ctx =
-         (SilcClientKEInternalContext *)sock->protocol->context;
-
-       if (proto_ctx->packet)
-         silc_packet_context_free(proto_ctx->packet);
-
-       proto_ctx->packet = silc_packet_context_dup(packet);
-       proto_ctx->dest_id_type = packet->src_id_type;
-       proto_ctx->dest_id = silc_id_str2id(packet->src_id, packet->src_id_len,
-                                           packet->src_id_type);
-       if (!proto_ctx->dest_id)
-         break;
-
-       /* Let the protocol handle the packet */
-       silc_protocol_execute(sock->protocol, client->schedule, 0, 0);
-      }
-    } else {
-      SILC_LOG_ERROR(("Received Key Exchange 1 packet but no key exchange "
-                     "protocol active, packet dropped."));
-    }
-    break;
-
-  case SILC_PACKET_KEY_EXCHANGE_2:
-    if (sock->protocol && sock->protocol->protocol &&
-       (sock->protocol->protocol->type == SILC_PROTOCOL_CLIENT_KEY_EXCHANGE ||
-        sock->protocol->protocol->type == SILC_PROTOCOL_CLIENT_REKEY)) {
-
-      if (sock->protocol->protocol->type == SILC_PROTOCOL_CLIENT_REKEY) {
-       SilcClientRekeyInternalContext *proto_ctx =
-         (SilcClientRekeyInternalContext *)sock->protocol->context;
-
-       if (proto_ctx->packet)
-         silc_packet_context_free(proto_ctx->packet);
-
-       proto_ctx->packet = silc_packet_context_dup(packet);
-
-       /* Let the protocol handle the packet */
-       silc_protocol_execute(sock->protocol, client->schedule, 0, 0);
-      } else {
-       SilcClientKEInternalContext *proto_ctx =
-         (SilcClientKEInternalContext *)sock->protocol->context;
-
-       if (proto_ctx->packet)
-         silc_packet_context_free(proto_ctx->packet);
-        if (proto_ctx->dest_id)
-          silc_free(proto_ctx->dest_id);
-       proto_ctx->packet = silc_packet_context_dup(packet);
-       proto_ctx->dest_id_type = packet->src_id_type;
-       proto_ctx->dest_id = silc_id_str2id(packet->src_id, packet->src_id_len,
-                                           packet->src_id_type);
-       if (!proto_ctx->dest_id)
-         break;
-
-       /* Let the protocol handle the packet */
-       silc_protocol_execute(sock->protocol, client->schedule, 0, 0);
-      }
-    } else {
-      SILC_LOG_ERROR(("Received Key Exchange 2 packet but no key exchange "
-                     "protocol active, packet dropped."));
-    }
-    break;
-
-  case SILC_PACKET_NEW_ID:
-    {
-      /*
-       * Received new ID from server. This packet is received at
-       * the connection to the server.  New ID is also received when
-       * user changes nickname but in that case the new ID is received
-       * as command reply and not as this packet type.
-       */
-      SilcIDPayload idp;
-
-      idp = silc_id_payload_parse(buffer->data, buffer->len);
-      if (!idp)
-       break;
-      if (silc_id_payload_get_type(idp) != SILC_ID_CLIENT)
-       break;
-
-      silc_client_receive_new_id(client, sock, idp);
-      silc_id_payload_free(idp);
-      break;
-    }
-
-  case SILC_PACKET_HEARTBEAT:
-    /*
-     * Received heartbeat packet
-     */
-    SILC_LOG_DEBUG(("Heartbeat packet"));
-    break;
-
-  case SILC_PACKET_KEY_AGREEMENT:
-    /*
-     * Received key agreement packet
-     */
-    SILC_LOG_DEBUG(("Key agreement packet"));
-    silc_client_key_agreement(client, sock, packet);
-    break;
-
-  case SILC_PACKET_REKEY:
-    SILC_LOG_DEBUG(("Re-key packet"));
-    /* We ignore this for now */
-    break;
-
-  case SILC_PACKET_REKEY_DONE:
-    SILC_LOG_DEBUG(("Re-key done packet"));
-
-    if (sock->protocol && sock->protocol->protocol &&
-       sock->protocol->protocol->type == SILC_PROTOCOL_CLIENT_REKEY) {
-
-      SilcClientRekeyInternalContext *proto_ctx =
-       (SilcClientRekeyInternalContext *)sock->protocol->context;
-
-      if (proto_ctx->packet)
-       silc_packet_context_free(proto_ctx->packet);
-
-      proto_ctx->packet = silc_packet_context_dup(packet);
-
-      /* Let the protocol handle the packet */
-      if (proto_ctx->responder == FALSE)
-       silc_protocol_execute(sock->protocol, client->schedule, 0, 0);
-      else
-       /* Let the protocol handle the packet */
-       silc_protocol_execute(sock->protocol, client->schedule,
-                             0, 100000);
-    } else {
-      SILC_LOG_ERROR(("Received Re-key done packet but no re-key "
-                     "protocol active, packet dropped."));
-    }
-    break;
-
-  case SILC_PACKET_CONNECTION_AUTH_REQUEST:
-    /*
-     * Reveived reply to our connection authentication method request
-     * packet. This is used to resolve the authentication method for the
-     * current session from the server if the client does not know it.
-     */
-    silc_client_connection_auth_request(client, sock, packet);
-    break;
-
-  case SILC_PACKET_FTP:
-    /* Received file transfer packet. */
-    silc_client_ftp(client, sock, packet);
-    break;
-
-  default:
-    SILC_LOG_DEBUG(("Incorrect packet type %d, packet dropped", type));
-    break;
-  }
-}
-
-/* Sends packet. This doesn't actually send the packet instead it assembles
-   it and marks it to be sent. However, if force_send is TRUE the packet
-   is sent immediately. if dst_id, cipher and hmac are NULL those parameters
-   will be derived from sock argument. Otherwise the valid arguments sent
-   are used. */
-
-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,
-                            SilcUInt32 data_len,
-                            SilcBool force_send)
-{
-  SilcPacketContext packetdata;
-  const SilcBufferStruct packet;
-  int block_len;
-  SilcUInt32 sequence = 0;
-
-  if (!sock)
-    return;
-
-  SILC_LOG_DEBUG(("Sending packet, type %d", type));
-
-  /* Get data used in the packet sending, keys and stuff */
-  if ((!cipher || !hmac || !dst_id) && sock->user_data) {
-    if (!cipher && ((SilcClientConnection)sock->user_data)->internal->send_key)
-      cipher = ((SilcClientConnection)sock->user_data)->internal->send_key;
-
-    if (!hmac && ((SilcClientConnection)sock->user_data)->internal->hmac_send)
-      hmac = ((SilcClientConnection)sock->user_data)->internal->hmac_send;
+  }
+}
 
-    if (!dst_id && ((SilcClientConnection)sock->user_data)->remote_id) {
-      dst_id = ((SilcClientConnection)sock->user_data)->remote_id;
-      dst_id_type = SILC_ID_SERVER;
-    }
+/* Parser callback called by silc_packet_receive_process. Thie merely
+   registers timeout that will handle the actual parsing when appropriate. */
 
-    if (hmac)
-      sequence = ((SilcClientConnection)sock->user_data)->internal->psn_send++;
+static SilcBool silc_client_packet_parse(SilcPacketParserContext *parser_context,
+                                    void *context)
+{
+  SilcClient client = (SilcClient)context;
+  SilcClientConnection conn = parser_context->sock;
+  SilcClientConnection conn = (SilcClientConnection)sock->user_data;
+  SilcPacketContext *packet = parser_context->packet;
+  SilcPacketType ret;
 
-    /* Check for mandatory rekey */
-    if (sequence == SILC_CLIENT_REKEY_THRESHOLD)
-      silc_schedule_task_add(client->schedule, sock->sock,
-                            silc_client_rekey_callback, sock, 0, 1,
-                            SILC_TASK_TIMEOUT, SILC_TASK_PRI_NORMAL);
-  }
+  if (conn && conn->internal->hmac_receive && conn->sock == sock)
+    conn->internal->psn_receive = parser_context->packet->sequence + 1;
 
-  block_len = cipher ? silc_cipher_get_block_len(cipher) : 0;
-
-  /* Set the packet context pointers */
-  packetdata.flags = 0;
-  packetdata.type = type;
-  if (sock->user_data &&
-      ((SilcClientConnection)sock->user_data)->local_id_data) {
-    packetdata.src_id = ((SilcClientConnection)sock->user_data)->local_id_data;
-    packetdata.src_id_len =
-      silc_id_get_len(((SilcClientConnection)sock->user_data)->local_id,
-                     SILC_ID_CLIENT);
-  } else {
-    packetdata.src_id = silc_calloc(SILC_ID_CLIENT_LEN, sizeof(unsigned char));
-    packetdata.src_id_len = SILC_ID_CLIENT_LEN;
-  }
-  packetdata.src_id_type = SILC_ID_CLIENT;
-  if (dst_id) {
-    packetdata.dst_id = silc_id_id2str(dst_id, dst_id_type);
-    packetdata.dst_id_len = silc_id_get_len(dst_id, dst_id_type);
-    packetdata.dst_id_type = dst_id_type;
-  } else {
-    packetdata.dst_id = NULL;
-    packetdata.dst_id_len = 0;
-    packetdata.dst_id_type = SILC_ID_NONE;
-  }
-  data_len = SILC_PACKET_DATALEN(data_len, (SILC_PACKET_HEADER_LEN +
-                                           packetdata.src_id_len +
-                                           packetdata.dst_id_len));
-  packetdata.truelen = data_len + SILC_PACKET_HEADER_LEN +
-    packetdata.src_id_len + packetdata.dst_id_len;
-  if (type == SILC_PACKET_CONNECTION_AUTH)
-    SILC_PACKET_PADLEN_MAX(packetdata.truelen, block_len, packetdata.padlen);
+  /* Parse the packet immediately */
+  if (parser_context->normal)
+    ret = silc_packet_parse(packet, conn->internal->receive_key);
   else
-    SILC_PACKET_PADLEN(packetdata.truelen, block_len, packetdata.padlen);
+    ret = silc_packet_parse_special(packet, conn->internal->receive_key);
 
-  /* Create the outgoing packet */
-  if (!silc_packet_assemble(&packetdata, client->rng, cipher, hmac, sock,
-                            data, data_len, (const SilcBuffer)&packet)) {
-    SILC_LOG_ERROR(("Error assembling packet"));
-    return;
+  if (ret == SILC_PACKET_NONE) {
+    silc_packet_context_free(packet);
+    silc_free(parser_context);
+    return FALSE;
   }
 
-  /* Encrypt the packet */
-  if (cipher)
-    silc_packet_encrypt(cipher, hmac, sequence, (SilcBuffer)&packet,
-                        packet.len);
-
-  SILC_LOG_HEXDUMP(("Packet (%d), len %d", sequence, packet.len),
-                  packet.data, packet.len);
+  /* If protocol for this connection is key exchange or rekey then we'll
+     process all packets synchronously, since there might be packets in
+     queue that we are not able to decrypt without first processing the
+     packets before them. */
+  if (sock->protocol && sock->protocol->protocol &&
+      (sock->protocol->protocol->type == SILC_PROTOCOL_CLIENT_KEY_EXCHANGE ||
+       sock->protocol->protocol->type == SILC_PROTOCOL_CLIENT_REKEY)) {
 
-  /* Now actually send the packet */
-  silc_client_packet_send_real(client, sock, force_send);
-}
+    /* Parse the incoming packet type */
+    silc_client_packet_parse_type(client, sock, packet);
 
-/* Packet sending routine for application.  This is the only routine that
-   is provided for application to send SILC packets. */
+    /* Reprocess the buffer since we'll return FALSE. This is because
+       the `conn->internal->receive_key' might have become valid by processing
+       the previous packet */
+    if (sock->type != SILC_SOCKET_TYPE_UNKNOWN)
+      silc_packet_receive_process(sock, FALSE, conn->internal->receive_key,
+                                 conn->internal->hmac_receive,
+                                 conn->internal->psn_receive,
+                                 silc_client_packet_parse, client);
+    else
+      silc_packet_receive_process(sock, FALSE, NULL, NULL, 0,
+                                 silc_client_packet_parse, client);
 
-SilcBool silc_client_send_packet(SilcClient client,
-                            SilcClientConnection conn,
-                            SilcPacketType type,
-                            const unsigned char *data,
-                            SilcUInt32 data_len)
-{
+    silc_packet_context_free(packet);
+    silc_free(parser_context);
 
-  assert(client);
-  if (!conn)
     return FALSE;
+  }
 
-  silc_client_packet_send(client, conn->sock, type, NULL, 0, NULL, NULL,
-                         (unsigned char *)data, data_len, TRUE);
+  /* Parse the incoming packet type */
+  silc_client_packet_parse_type(client, sock, packet);
+  silc_packet_context_free(packet);
+  silc_free(parser_context);
   return TRUE;
 }
-
-void silc_client_packet_queue_purge(SilcClient client,
-                                   SilcSocketConnection sock)
-{
-  if (sock && SILC_IS_OUTBUF_PENDING(sock) &&
-      !(SILC_IS_DISCONNECTED(sock))) {
-    int ret;
-
-    ret = silc_packet_send(sock, TRUE);
-    if (ret == -2) {
-      if (sock->outbuf && sock->outbuf->len > 0) {
-       /* Couldn't send all data, put the queue back up, we'll send
-          rest later. */
-       SILC_CLIENT_SET_CONNECTION_FOR_OUTPUT(client->schedule, sock->sock);
-       SILC_SET_OUTBUF_PENDING(sock);
-       return;
-      }
-    }
-
-    /* Purged all data */
-    SILC_UNSET_OUTBUF_PENDING(sock);
-    SILC_CLIENT_SET_CONNECTION_FOR_INPUT(client->schedule, sock->sock);
-    silc_buffer_clear(sock->outbuf);
-  }
-}
+#endif /* 0 */
 
 /* Closes connection to remote end. Free's all allocated data except
    for some information such as nickname etc. that are valid at all time.
@@ -1553,8 +939,8 @@ void silc_client_packet_queue_purge(SilcClient client,
    connection but `conn->sock' might be actually a different connection
    than the `sock'). */
 
+#if 0
 void silc_client_close_connection_real(SilcClient client,
-                                      SilcSocketConnection sock,
                                       SilcClientConnection conn)
 {
   int del = FALSE;
@@ -1614,32 +1000,16 @@ void silc_client_close_connection_real(SilcClient client,
 void silc_client_close_connection(SilcClient client,
                                  SilcClientConnection conn)
 {
-  silc_client_close_connection_real(client, NULL, conn);
-}
-
-/* Called when we receive disconnection packet from server. This
-   closes our end properly and displays the reason of the disconnection
-   on the screen. */
-
-SILC_TASK_CALLBACK(silc_client_disconnected_by_server_later)
-{
-  SilcClient client = (SilcClient)context;
-  SilcSocketConnection sock;
-
-  SILC_CLIENT_GET_SOCK(client, fd, sock);
-  if (sock == NULL)
-    return;
-
-  silc_client_close_connection_real(client, sock, sock->user_data);
+  //  silc_client_close_connection_real(client, NULL, conn);
 }
 
 /* Called when we receive disconnection packet from server. This
    closes our end properly and displays the reason of the disconnection
    on the screen. */
 
-void silc_client_disconnected_by_server(SilcClient client,
-                                       SilcSocketConnection sock,
-                                       SilcBuffer packet)
+void silc_client_disconnect(SilcClient client,
+                           SilcClientConnection conn,
+                           SilcBuffer packet)
 {
   SilcClientConnection conn;
   SilcStatus status;
@@ -1675,7 +1045,7 @@ void silc_client_disconnected_by_server(SilcClient client,
    We don't take any action what so ever of the error message. */
 
 void silc_client_error_by_server(SilcClient client,
-                                SilcSocketConnection sock,
+                                SilcClientConnection conn,
                                 SilcBuffer message)
 {
   char *msg;
@@ -1732,7 +1102,7 @@ static void silc_client_resume_session_cb(SilcClient client,
    deleted from cache and new one is added. */
 
 void silc_client_receive_new_id(SilcClient client,
-                               SilcSocketConnection sock,
+                               SilcClientConnection conn,
                                SilcIDPayload idp)
 {
   SilcClientConnection conn = (SilcClientConnection)sock->user_data;
@@ -1791,6 +1161,7 @@ void silc_client_receive_new_id(SilcClient client,
   silc_idcache_add(conn->internal->client_cache, nickname, conn->local_id,
                   (void *)conn->local_entry, 0, NULL);
 
+#if 0
   if (connecting) {
     SilcBuffer sidp;
 
@@ -1835,6 +1206,7 @@ void silc_client_receive_new_id(SilcClient client,
                                 NULL);
     }
   }
+#endif /* 0 */
 }
 
 /* Removes a client entry from all channels it has joined. */
@@ -1886,7 +1258,7 @@ void silc_client_replace_from_channels(SilcClient client,
    with timeout. */
 
 void silc_client_process_failure(SilcClient client,
-                                SilcSocketConnection sock,
+                                SilcClientConnection conn,
                                 SilcPacketContext *packet)
 {
   SilcUInt32 failure = 0;
@@ -1906,7 +1278,7 @@ void silc_client_process_failure(SilcClient client,
 
 SILC_TASK_CALLBACK_GLOBAL(silc_client_rekey_callback)
 {
-  SilcSocketConnection sock = (SilcSocketConnection)context;
+  SilcClientConnection conn = (SilcSocketConnection)context;
   SilcClientConnection conn = (SilcClientConnection)sock->user_data;
   SilcClient client = (SilcClient)conn->internal->rekey->context;
   SilcProtocol protocol;
@@ -1946,7 +1318,7 @@ SILC_TASK_CALLBACK(silc_client_rekey_final)
   SilcClientRekeyInternalContext *ctx =
     (SilcClientRekeyInternalContext *)protocol->context;
   SilcClient client = (SilcClient)ctx->client;
-  SilcSocketConnection sock = ctx->sock;
+  SilcClientConnection conn = ctx->sock;
   SilcClientConnection conn = (SilcClientConnection)sock->user_data;
 
   SILC_LOG_DEBUG(("Start"));
@@ -1994,7 +1366,7 @@ SILC_TASK_CALLBACK(silc_client_rekey_final)
    client does not know it beforehand. */
 
 void silc_client_connection_auth_request(SilcClient client,
-                                        SilcSocketConnection sock,
+                                        SilcClientConnection conn,
                                         SilcPacketContext *packet)
 {
   SilcClientConnection conn = (SilcClientConnection)sock->user_data;
@@ -2093,3 +1465,199 @@ silc_client_request_authentication_method(SilcClient client,
                           client->internal->params->connauth_request_secs, 0,
                           SILC_TASK_TIMEOUT, SILC_TASK_PRI_NORMAL);
 }
+#endif /* 0 */
+
+
+/******************************* Client API *********************************/
+
+/* Allocates new client object. This has to be done before client may
+   work. After calling this one must call silc_client_init to initialize
+   the client. The `application' is application specific user data pointer
+   and caller must free it. */
+
+SilcClient silc_client_alloc(SilcClientOperations *ops,
+                            SilcClientParams *params,
+                            void *application,
+                            const char *version_string)
+{
+  SilcClient new_client;
+
+  new_client = silc_calloc(1, sizeof(*new_client));
+  new_client->application = application;
+
+  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));
+  if (!version_string)
+    version_string = silc_version_string;
+  new_client->internal->silc_client_version = strdup(version_string);
+
+  if (params)
+    memcpy(new_client->internal->params, params, sizeof(*params));
+
+  if (!new_client->internal->params->task_max)
+    new_client->internal->params->task_max = 200;
+
+  if (!new_client->internal->params->rekey_secs)
+    new_client->internal->params->rekey_secs = 3600;
+
+  if (!new_client->internal->params->connauth_request_secs)
+    new_client->internal->params->connauth_request_secs = 2;
+
+  new_client->internal->params->
+    nickname_format[sizeof(new_client->internal->
+                          params->nickname_format) - 1] = 0;
+
+  return new_client;
+}
+
+/* Frees client object and its internals. */
+
+void silc_client_free(SilcClient client)
+{
+  if (client) {
+    if (client->rng)
+      silc_rng_free(client->rng);
+
+    if (!client->internal->params->dont_register_crypto_library) {
+      silc_cipher_unregister_all();
+      silc_pkcs_unregister_all();
+      silc_hash_unregister_all();
+      silc_hmac_unregister_all();
+    }
+
+    silc_hash_free(client->md5hash);
+    silc_hash_free(client->sha1hash);
+    silc_hmac_free(client->internal->md5hmac);
+    silc_hmac_free(client->internal->sha1hmac);
+    silc_free(client->internal->params);
+    silc_free(client->internal->silc_client_version);
+    silc_free(client->internal);
+    silc_free(client);
+  }
+}
+
+/* Initializes the client. This makes all the necessary steps to make
+   the client ready to be run. One must call silc_client_run to run the
+   client. Returns FALSE if error occured, TRUE otherwise. */
+
+SilcBool silc_client_init(SilcClient client)
+{
+  SILC_LOG_DEBUG(("Initializing client"));
+
+  assert(client);
+  assert(client->username);
+  assert(client->hostname);
+  assert(client->realname);
+
+  /* Validate essential strings */
+  if (client->nickname)
+    if (!silc_identifier_verify(client->nickname, strlen(client->nickname),
+                               SILC_STRING_UTF8, 128)) {
+      SILC_LOG_ERROR(("Malformed nickname '%s'", client->nickname));
+      return FALSE;
+    }
+  if (!silc_identifier_verify(client->username, strlen(client->username),
+                             SILC_STRING_UTF8, 128)) {
+    SILC_LOG_ERROR(("Malformed username '%s'", client->username));
+    return FALSE;
+  }
+  if (!silc_identifier_verify(client->hostname, strlen(client->hostname),
+                             SILC_STRING_UTF8, 256)) {
+    SILC_LOG_ERROR(("Malformed hostname '%s'", client->hostname));
+    return FALSE;
+  }
+  if (!silc_utf8_valid(client->realname, strlen(client->realname))) {
+    SILC_LOG_ERROR(("Malformed realname '%s'", client->realname));
+    return FALSE;
+  }
+
+  if (!client->internal->params->dont_register_crypto_library) {
+    /* Initialize the crypto library.  If application has done this already
+       this has no effect.  Also, we will not be overriding something
+       application might have registered earlier. */
+    silc_cipher_register_default();
+    silc_pkcs_register_default();
+    silc_hash_register_default();
+    silc_hmac_register_default();
+  }
+
+  /* Initialize hash functions for client to use */
+  silc_hash_alloc("md5", &client->md5hash);
+  silc_hash_alloc("sha1", &client->sha1hash);
+
+  /* Initialize random number generator */
+  client->rng = silc_rng_alloc();
+  silc_rng_init(client->rng);
+  silc_rng_global_init(client->rng);
+
+  /* Initialize the scheduler */
+  client->schedule =
+    silc_schedule_init(client->internal->params->task_max ?
+                      client->internal->params->task_max : 200, client);
+  if (!client->schedule)
+    return FALSE;
+
+  /* Start packet engine */
+  client->internal->packet_engine =
+    silc_packet_engine_start(client->rng, FALSE, &silc_client_stream_cbs,
+                            client);
+  if (!client->internal->packet_engine)
+    return FALSE;
+
+  /* Initialize FSM */
+  if (!silc_fsm_init(&client->internal->fsm, client, NULL, NULL,
+                    client->schedule))
+    return FALSE;
+  silc_fsm_sema_init(&client->internal->wait_event, &client->internal->fsm, 0);
+
+  /* Allocate client lock */
+  silc_mutex_alloc(&client->internal->lock);
+
+  /* Register commands */
+  silc_client_commands_register(client);
+
+  return TRUE;
+}
+
+/* Stops the client. This is called to stop the client and thus to stop
+   the program. */
+
+void silc_client_stop(SilcClient client)
+{
+  SILC_LOG_DEBUG(("Stopping client"));
+
+  silc_schedule_stop(client->schedule);
+  silc_schedule_uninit(client->schedule);
+
+  silc_client_commands_unregister(client);
+
+  SILC_LOG_DEBUG(("Client stopped"));
+}
+
+/* Starts the SILC client FSM machine and blocks here.  When this returns
+   the client has ended. */
+
+void silc_client_run(SilcClient client)
+{
+  SILC_LOG_DEBUG(("Starting SILC client"));
+
+  /* Start the client */
+  silc_fsm_start_sync(&client->internal->fsm, silc_client_st_run);
+
+  /* Signal the application when we are running */
+  client->internal->run_callback = TRUE;
+  SILC_FSM_SEMA_POST(&client->internal->wait_event);
+
+  /* Run the scheduler */
+  silc_schedule(client->schedule);
+}
+
+/* Call scheduler one iteration and return.  This cannot be called if threads
+   are in use. */
+
+void silc_client_run_one(SilcClient client)
+{
+  silc_schedule_one(client->schedule, -1);
+}
index 0780823abd36ae8c5ba79b6c7bdbcba7a410f33e..59bb12b1c73c971d9147c6a4209eebbb393a8d76 100644 (file)
@@ -1,10 +1,10 @@
 /*
 
-  client.h 
+  client.h
 
   Author: Pekka Riikonen <priikone@silcnet.org>
 
-  Copyright (C) 1997 - 2002 Pekka Riikonen
+  Copyright (C) 1997 - 2006 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
 #ifndef CLIENT_H
 #define CLIENT_H
 
+#ifndef SILCCLIENT_H
+#error "Do not include this header directly"
+#endif
+
 /* Forward declarations */
 typedef struct SilcClientStruct *SilcClient;
 typedef struct SilcClientConnectionStruct *SilcClientConnection;
@@ -30,20 +34,33 @@ typedef struct SilcClientFtpSessionStruct *SilcClientFtpSession;
 typedef struct SilcClientEntryStruct *SilcClientEntry;
 typedef struct SilcChannelEntryStruct *SilcChannelEntry;
 typedef struct SilcServerEntryStruct *SilcServerEntry;
-typedef struct SilcClientCommandStruct *SilcClientCommand;
-typedef struct SilcClientCommandContextStruct *SilcClientCommandContext;
 typedef struct SilcClientCommandReplyContextStruct
                                            *SilcClientCommandReplyContext;
 typedef struct SilcChannelUserStruct *SilcChannelUser;
 typedef struct SilcClientInternalStruct *SilcClientInternal;
-typedef struct SilcClientConnectionInternalStruct 
+typedef struct SilcClientConnectionInternalStruct
                                           *SilcClientConnectionInternal;
 typedef struct SilcChannelPrivateKeyStruct *SilcChannelPrivateKey;
-  
-/* Client entry status */
-typedef enum {
-  SILC_CLIENT_STATUS_NONE       = 0x0000,
-  SILC_CLIENT_STATUS_RESOLVING  = 0x0001,
-} SilcEntryStatus;
 
-#endif
+
+/* Internal client entry context */
+typedef struct SilcClientEntryInternalStruct {
+  SilcCipher send_key;         /* Private message key for sending */
+  SilcCipher receive_key;      /* Private message key for receiving */
+  SilcHmac hmac_send;          /* Private mesage key HMAC for sending */
+  SilcHmac hmac_receive;       /* Private mesage key HMAC for receiving */
+  unsigned char *key;          /* Valid if application provided the key */
+  SilcUInt32 key_len;          /* Key data length */
+  SilcClientKeyAgreement ke;   /* Current key agreement context or NULL */
+
+  /* Flags */
+  unsigned int valid       : 1;        /* FALSE if this entry is not valid */
+  unsigned int resolving   : 1; /* TRUE when entry is being resolved */
+  unsigned int generated   : 1; /* TRUE if library generated `key' */
+  unsigned int prv_resp    : 1; /* TRUE if private message key indicator
+                                  has been received (responder). */
+  SilcUInt16 resolve_cmd_ident;        /* Command identifier when resolving */
+  SilcAtomic8 refcnt;          /* Reference counter */
+} SilcClientEntryInternal;
+
+#endif /* CLIENT_H */
index f29d91f495a3b8f7cfb082085c88ec24450098e0..207e34eb5995e2eca9f49ec1d96d1af672cca522 100644 (file)
@@ -4,7 +4,7 @@
 
   Author: Pekka Riikonen <priikone@silcnet.org>
 
-  Copyright (C) 2002 - 2004 Pekka Riikonen
+  Copyright (C) 2002 - 2004, 2006 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
@@ -74,10 +74,9 @@ static void silc_client_attributes_process_foreach(void *key, void *context,
 /* Process list of attributes.  Returns reply to the requested attributes. */
 
 SilcBuffer silc_client_attributes_process(SilcClient client,
-                                         SilcSocketConnection sock,
+                                         SilcClientConnection conn,
                                          SilcDList attrs)
 {
-  SilcClientConnection conn = sock->user_data;
   SilcBuffer buffer = NULL;
   SilcAttrForeach f;
   SilcAttribute attribute;
index 32d59862e0a1e2d1539f6331c816cc6f33b8cc2f..7587d07d27a9451fd5f2e54de2c4e8a37a5abce1 100644 (file)
@@ -4,7 +4,7 @@
 
   Author: Pekka Riikonen <priikone@silcnet.org>
 
-  Copyright (C) 1997 - 2004 Pekka Riikonen
+  Copyright (C) 1997 - 2004, 2006 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
@@ -599,8 +599,8 @@ SilcBool silc_client_add_channel_private_key(SilcClient client,
    on error, TRUE otherwise. */
 
 SilcBool silc_client_del_channel_private_keys(SilcClient client,
-                                        SilcClientConnection conn,
-                                        SilcChannelEntry channel)
+                                             SilcClientConnection conn,
+                                             SilcChannelEntry channel)
 {
   SilcChannelPrivateKey entry;
 
diff --git a/lib/silcclient/client_connect.c b/lib/silcclient/client_connect.c
new file mode 100644 (file)
index 0000000..95e086c
--- /dev/null
@@ -0,0 +1,508 @@
+/*
+
+  client_st_connect.c
+
+  Author: Pekka Riikonen <priikone@silcnet.org>
+
+  Copyright (C) 2006 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; 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.
+
+*/
+
+#include "silc.h"
+#include "silcclient.h"
+#include "client_internal.h"
+
+/************************** Types and definitions ***************************/
+
+/* Public key verification context */
+typedef struct {
+  SilcSKE ske;
+  SilcSKEVerifyCbCompletion completion;
+  void *completion_context;
+} *VerifyKeyContext;
+
+
+/************************ Static utility functions **************************/
+
+/* Callback called after connected to remote host */
+
+static void silc_client_connect_callback(SilcNetStatus status,
+                                        SilcStream stream, void *context)
+{
+  SilcFSMThread fsm = context;
+  SilcClientConnection conn = silc_fsm_get_context(fsm);
+  SilcClient client = conn->client;
+
+  if (conn->internal->verbose) {
+    switch (status) {
+    case SILC_NET_OK:
+      break;
+    case SILC_NET_UNKNOWN_IP:
+      client->internal->ops->say(
+                  client, conn, SILC_CLIENT_MESSAGE_ERROR,
+                  "Could not connect to host %s: unknown IP address",
+                  conn->remote_host);
+      break;
+    case SILC_NET_UNKNOWN_HOST:
+      client->internal->ops->say(
+                  client, conn, SILC_CLIENT_MESSAGE_ERROR,
+                  "Could not connect to host %s: unknown host name",
+                  conn->remote_host);
+      break;
+    case SILC_NET_HOST_UNREACHABLE:
+      client->internal->ops->say(
+                  client, conn, SILC_CLIENT_MESSAGE_ERROR,
+                  "Could not connect to host %s: network unreachable",
+                  conn->remote_host);
+      break;
+    case SILC_NET_CONNECTION_REFUSED:
+      client->internal->ops->say(
+                  client, conn, SILC_CLIENT_MESSAGE_ERROR,
+                  "Could not connect to host %s: connection refused",
+                  conn->remote_host);
+      break;
+    case SILC_NET_CONNECTION_TIMEOUT:
+      client->internal->ops->say(
+                  client, conn, SILC_CLIENT_MESSAGE_ERROR,
+                  "Could not connect to host %s: connection timeout",
+                  conn->remote_host);
+      break;
+    default:
+      client->internal->ops->say(
+                  client, conn, SILC_CLIENT_MESSAGE_ERROR,
+                  "Could not connect to host %s",
+                  conn->remote_host);
+      break;
+    }
+  }
+
+  if (status != SILC_NET_OK) {
+    /* Notify application of failure */
+    SILC_LOG_DEBUG(("Connecting failed"));
+    conn->callback(client, conn, SILC_CLIENT_CONN_ERROR, conn->context);
+    silc_fsm_next(fsm, silc_client_st_connect_error);
+    SILC_FSM_CALL_CONTINUE(fsm);
+    return;
+  }
+
+  SILC_LOG_DEBUG(("Connected"));
+
+  /* Create packet stream */
+  conn->stream = silc_packet_stream_create(client->internal->packet_engine,
+                                          conn->internal->schedule, stream);
+  if (!conn->stream) {
+    SILC_LOG_DEBUG(("Could not create packet stream"));
+    conn->callback(client, conn, SILC_CLIENT_CONN_ERROR, conn->context);
+    silc_fsm_next(fsm, silc_client_st_connect_error);
+    SILC_FSM_CALL_CONTINUE(fsm);
+    return;
+  }
+
+  silc_packet_set_context(conn->stream, conn);
+
+  /* Connection created successfully */
+  SILC_FSM_CALL_CONTINUE(fsm);
+}
+
+/* Called after application has verified remote host's public key */
+
+static void silc_client_ke_verify_key_cb(SilcBool success, void *context)
+{
+  VerifyKeyContext verify = (VerifyKeyContext)context;
+
+  SILC_LOG_DEBUG(("Start"));
+
+  /* Call the completion callback back to the SKE */
+  verify->completion(verify->ske, success ? SILC_SKE_STATUS_OK :
+                    SILC_SKE_STATUS_UNSUPPORTED_PUBLIC_KEY,
+                    verify->completion_context);
+
+  silc_free(verify);
+}
+
+/* Verify remote host's public key */
+
+static void silc_client_ke_verify_key(SilcSKE ske,
+                                     SilcPublicKey public_key,
+                                     void *context,
+                                     SilcSKEVerifyCbCompletion completion,
+                                     void *completion_context)
+{
+  SilcFSMThread fsm = context;
+  SilcClientConnection conn = silc_fsm_get_context(fsm);
+  SilcClient client = conn->client;
+  VerifyKeyContext verify;
+
+  /* If we provided repository for SKE and we got here the key was not
+     found from the repository. */
+  if (conn->internal->params.repository &&
+      !conn->internal->params.verify_notfound) {
+    completion(ske, SILC_SKE_STATUS_UNSUPPORTED_PUBLIC_KEY,
+              completion_context);
+    return;
+  }
+
+  SILC_LOG_DEBUG(("Verify remote public key"));
+
+  verify = silc_calloc(1, sizeof(*verify));
+  if (!verify) {
+    completion(ske, SILC_SKE_STATUS_UNSUPPORTED_PUBLIC_KEY,
+              completion_context);
+    return;
+  }
+  verify->ske = ske;
+  verify->completion = completion;
+  verify->completion_context = completion_context;
+
+  /* Verify public key in application */
+  client->internal->ops->verify_public_key(client, conn,
+                                          conn->type, public_key,
+                                          silc_client_ke_verify_key_cb,
+                                          verify);
+}
+
+/* Key exchange protocol completion callback */
+
+static void silc_client_ke_completion(SilcSKE ske,
+                                     SilcSKEStatus status,
+                                     SilcSKESecurityProperties prop,
+                                     SilcSKEKeyMaterial keymat,
+                                     SilcSKERekeyMaterial rekey,
+                                     void *context)
+{
+  SilcFSMThread fsm = context;
+  SilcClientConnection conn = silc_fsm_get_context(fsm);
+  SilcClient client = conn->client;
+  SilcCipher send_key, receive_key;
+  SilcHmac hmac_send, hmac_receive;
+
+  if (status != SILC_SKE_STATUS_OK) {
+    /* Key exchange failed */
+    SILC_LOG_DEBUG(("Error during key exchange with %s: %s (%d)",
+                   conn->remote_host, silc_ske_map_status(status), status));
+
+    if (conn->internal->verbose)
+      client->internal->ops->say(client, conn, SILC_CLIENT_MESSAGE_ERROR,
+                                "Error during key exchange with %s: %s",
+                                conn->remote_host,
+                                silc_ske_map_status(status));
+
+    silc_fsm_next(fsm, silc_client_st_connect_error);
+    SILC_FSM_CALL_CONTINUE(fsm);
+    return;
+  }
+
+  SILC_LOG_DEBUG(("Setting keys into use"));
+
+  /* Set the keys into use.  Data will be encrypted after this. */
+  if (!silc_ske_set_keys(ske, keymat, prop, &send_key, &receive_key,
+                        &hmac_send, &hmac_receive, &conn->internal->hash)) {
+    /* Error setting keys */
+    SILC_LOG_DEBUG(("Could not set keys into use"));
+
+    if (conn->internal->verbose)
+      client->internal->ops->say(
+                      client, conn, SILC_CLIENT_MESSAGE_ERROR,
+                      "Error during key exchange with %s: cannot use keys",
+                      conn->remote_host);
+
+    conn->callback(client, conn, SILC_CLIENT_CONN_ERROR_KE, conn->context);
+
+    silc_fsm_next(fsm, silc_client_st_connect_error);
+    SILC_FSM_CALL_CONTINUE(fsm);
+    return;
+  }
+
+  silc_packet_set_ciphers(conn->stream, send_key, receive_key);
+  silc_packet_set_hmacs(conn->stream, hmac_send, hmac_receive);
+
+  conn->internal->rekey = rekey;
+
+  /* Key exchange done */
+  SILC_FSM_CALL_CONTINUE(fsm);
+}
+
+/* Callback called by application to return authentication data */
+
+static void silc_client_connect_auth_method(SilcBool success,
+                                           SilcAuthMethod auth_meth,
+                                           void *auth, SilcUInt32 auth_len,
+                                           void *context)
+{
+  SilcFSMThread fsm = context;
+  SilcClientConnection conn = silc_fsm_get_context(fsm);
+
+  conn->internal->params.auth_method = SILC_AUTH_NONE;
+
+  if (success) {
+    conn->internal->params.auth_method = auth_meth;
+    conn->internal->params.auth = auth;
+    conn->internal->params.auth_len = auth_len;
+  }
+
+  SILC_FSM_CALL_CONTINUE(fsm);
+}
+
+/* Connection authentication completion callback */
+
+static void silc_client_connect_auth_completion(SilcConnAuth connauth,
+                                               SilcBool success,
+                                               void *context)
+{
+  SilcFSMThread fsm = context;
+  SilcClientConnection conn = silc_fsm_get_context(fsm);
+  SilcClient client = conn->client;
+
+  silc_connauth_free(connauth);
+
+  if (!success) {
+    if (conn->internal->verbose)
+       client->internal->ops->say(
+                       client, conn, SILC_CLIENT_MESSAGE_ERROR,
+                       "Authentication failed");
+
+    conn->callback(client, conn, SILC_CLIENT_CONN_ERROR_AUTH, conn->context);
+    silc_fsm_next(fsm, silc_client_st_connect_error);
+  }
+
+  SILC_FSM_CALL_CONTINUE(fsm);
+}
+
+/*************************** Connect remote host ****************************/
+
+/* Creates a connection to remote host */
+
+SILC_FSM_STATE(silc_client_st_connect)
+{
+  SilcClientConnection conn = fsm_context;
+  SilcClient client = conn->client;
+
+  SILC_LOG_DEBUG(("Connecting to %s:%d", conn->remote_host,
+                 conn->remote_port));
+
+  silc_fsm_next(fsm, silc_client_st_connect_key_exchange);
+
+  if (conn->internal->params.udp) {
+    SilcStream stream;
+
+    if (!conn->internal->params.local_ip) {
+      /** IP address not given */
+      SILC_LOG_ERROR(("Local UDP IP address not specified"));
+      conn->callback(client, conn, SILC_CLIENT_CONN_ERROR, conn->context);
+      silc_fsm_next(fsm, silc_client_st_connect_error);
+      return SILC_FSM_CONTINUE;
+    }
+
+    /** Connect (UDP) */
+    stream = silc_net_udp_connect(conn->internal->params.local_ip,
+                                 conn->internal->params.local_port,
+                                 conn->remote_host, conn->remote_port,
+                                 conn->internal->schedule);
+
+    SILC_FSM_CALL(silc_client_connect_callback(stream ? SILC_NET_OK :
+                                              SILC_NET_HOST_UNREACHABLE,
+                                              stream, fsm));
+  } else {
+    /** Connect (TCP) */
+    SILC_FSM_CALL(silc_net_tcp_connect(NULL, conn->remote_host,
+                                      conn->remote_port,
+                                      conn->internal->schedule,
+                                      silc_client_connect_callback, fsm));
+  }
+}
+
+/* Starts key exchange protocol with remote host */
+
+SILC_FSM_STATE(silc_client_st_connect_key_exchange)
+{
+  SilcClientConnection conn = fsm_context;
+  SilcClient client = conn->client;
+  SilcSKEParamsStruct params;
+
+  SILC_LOG_DEBUG(("Starting key exchange protocol"));
+
+  /* Allocate SKE */
+  conn->internal->ske =
+    silc_ske_alloc(client->rng, conn->internal->schedule,
+                  conn->internal->params.repository,
+                  conn->public_key, conn->private_key, fsm);
+  if (!conn->internal->ske) {
+    /** Out of memory */
+    conn->callback(client, conn, SILC_CLIENT_CONN_ERROR_KE, conn->context);
+    silc_fsm_next(fsm, silc_client_st_connect_error);
+    return SILC_FSM_CONTINUE;
+  }
+
+  /* Set SKE callbacks */
+  silc_ske_set_callbacks(conn->internal->ske, silc_client_ke_verify_key,
+                        silc_client_ke_completion, fsm);
+
+  /* Set up key exchange parameters */
+  params.version = client->internal->silc_client_version;
+  params.flags = SILC_SKE_SP_FLAG_MUTUAL;
+  if (conn->internal->params.pfs)
+    params.flags |= SILC_SKE_SP_FLAG_PFS;
+  if (conn->internal->params.udp) {
+    params.flags |= SILC_SKE_SP_FLAG_IV_INCLUDED;
+    params.session_port = conn->internal->params.local_port;
+  }
+
+  /** Start key exchange */
+  if (conn->internal->params.no_authentication)
+    silc_fsm_next(fsm, silc_client_st_connected);
+  else if (conn->internal->params.udp)
+    silc_fsm_next(fsm, silc_client_st_connect_setup_udp);
+  else
+    silc_fsm_next(fsm, silc_client_st_connect_auth);
+
+  SILC_FSM_CALL(silc_ske_initiator(conn->internal->ske, conn->stream,
+                                  &params, NULL));
+}
+
+/* For UDP/IP connections, set up the UDP session after successful key
+   exchange protocol */
+
+SILC_FSM_STATE(silc_client_st_connect_setup_udp)
+{
+  SilcClientConnection conn = fsm_context;
+  SilcClient client = conn->client;
+  SilcStream stream, old;
+  SilcSKESecurityProperties prop;
+
+  SILC_LOG_DEBUG(("Setup UDP SILC session"));
+
+  /* Create new UDP stream */
+  prop = silc_ske_get_security_properties(conn->internal->ske);
+  stream = silc_net_udp_connect(conn->internal->params.local_ip,
+                               conn->internal->params.local_port,
+                               conn->remote_host, prop->remote_port,
+                               conn->internal->schedule);
+  if (!stream) {
+    /** Cannot create UDP stream */
+    conn->callback(client, conn, SILC_CLIENT_CONN_ERROR, conn->context);
+    silc_fsm_next(fsm, silc_client_st_connect_error);
+    return SILC_FSM_CONTINUE;
+  }
+
+  /* Set the new stream to packet stream */
+  old = silc_packet_stream_get_stream(conn->stream);
+  silc_packet_stream_set_stream(conn->stream, stream,
+                               conn->internal->schedule);
+  silc_packet_stream_set_iv_included(conn->stream);
+  silc_packet_set_sid(conn->stream, 0);
+
+  /* Delete the old stream */
+  silc_stream_destroy(old);
+
+  /** Start authentication */
+  silc_fsm_next(fsm, silc_client_st_connect_auth);
+  return SILC_FSM_CONTINUE;
+}
+
+/* Get authentication method to be used in authentication protocol */
+
+SILC_FSM_STATE(silc_client_st_connect_auth)
+{
+  SilcClientConnection conn = fsm_context;
+  SilcClient client = conn->client;
+
+  SILC_LOG_DEBUG(("Get authentication data"));
+
+  silc_fsm_next(fsm, silc_client_st_connect_auth_start);
+
+  /* If authentication data not provided, ask from application */
+  if (!conn->internal->params.auth_set)
+    SILC_FSM_CALL(client->internal->ops->
+                 get_auth_method(client, conn,
+                                 conn->remote_host, conn->remote_port,
+                                 silc_client_connect_auth_method, fsm));
+
+  if (conn->internal->params.auth_method == SILC_AUTH_PUBLIC_KEY)
+    conn->internal->params.auth = conn->private_key;
+
+  /* We have authentication data */
+  return SILC_FSM_CONTINUE;
+}
+
+/* Start connection authentication with remote host */
+
+SILC_FSM_STATE(silc_client_st_connect_auth_start)
+{
+  SilcClientConnection conn = fsm_context;
+  SilcClient client = conn->client;
+  SilcConnAuth connauth;
+
+  SILC_LOG_DEBUG(("Starting connection authentication protocol"));
+
+  /* Allocate connection authentication protocol */
+  connauth = silc_connauth_alloc(conn->internal->schedule,
+                                conn->internal->ske,
+                                client->internal->params->rekey_secs);
+  if (!connauth) {
+    /** Out of memory */
+    conn->callback(client, conn, SILC_CLIENT_CONN_ERROR_AUTH, conn->context);
+    silc_fsm_next(fsm, silc_client_st_connect_error);
+    return SILC_FSM_CONTINUE;
+  }
+
+  /** Start connection authentication */
+  silc_fsm_next(fsm, silc_client_st_connected);
+  SILC_FSM_CALL(silc_connauth_initiator(connauth, SILC_CONN_CLIENT,
+                                       conn->internal->params.auth_method,
+                                       conn->internal->params.auth,
+                                       conn->internal->params.auth_len,
+                                       silc_client_connect_auth_completion,
+                                       fsm));
+}
+
+/* Connection fully established */
+
+SILC_FSM_STATE(silc_client_st_connected)
+{
+  SilcClientConnection conn = fsm_context;
+  SilcClient client = conn->client;
+
+  SILC_LOG_DEBUG(("Connection established"));
+
+  /* If we connected to server, now register to network. */
+  if (conn->type == SILC_CONN_SERVER &&
+      !conn->internal->params.no_authentication) {
+
+    /* If detach data is provided, resume the session. */
+    if (conn->internal->params.detach_data &&
+       conn->internal->params.detach_data_len) {
+      /** Resume detached session */
+      silc_fsm_next(fsm, silc_client_st_resume);
+    } else {
+      /** Register to network */
+      silc_fsm_next(fsm, silc_client_st_register);
+    }
+
+    return SILC_FSM_CONTINUE;
+  }
+
+  /* Call connection callback */
+  conn->callback(client, conn, SILC_CLIENT_CONN_SUCCESS, conn->context);
+
+  return SILC_FSM_FINISH;
+}
+
+/* Error during connecting */
+
+SILC_FSM_STATE(silc_client_st_connect_error)
+{
+
+  /* XXX */
+  /* Close connection */
+
+  return SILC_FSM_FINISH;
+}
diff --git a/lib/silcclient/client_connect.h b/lib/silcclient/client_connect.h
new file mode 100644 (file)
index 0000000..c375ae0
--- /dev/null
@@ -0,0 +1,32 @@
+/*
+
+  client_connect.h
+
+  Author: Pekka Riikonen <priikone@silcnet.org>
+
+  Copyright (C) 2006 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; 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.
+
+*/
+
+#ifndef CLIENT_CONNECT_H
+#define CLIENT_CONNECT_H
+
+/* States */
+SILC_FSM_STATE(silc_client_st_connect);
+SILC_FSM_STATE(silc_client_st_connect_key_exchange);
+SILC_FSM_STATE(silc_client_st_connect_setup_udp);
+SILC_FSM_STATE(silc_client_st_connect_auth);
+SILC_FSM_STATE(silc_client_st_connect_auth_start);
+SILC_FSM_STATE(silc_client_st_connected);
+SILC_FSM_STATE(silc_client_st_connect_error);
+
+#endif /* CLIENT_CONNECT_H */
similarity index 57%
rename from lib/silcclient/idlist.c
rename to lib/silcclient/client_entry.c
index 23674fc591fd42cd5ea803c7d6105c346021349b..4db54f03d12ca9459d3faf9305e2456bf11e3a1c 100644 (file)
@@ -1,10 +1,10 @@
 /*
 
-  idlist.c
+  client_entry.c
 
   Author: Pekka Riikonen <priikone@silcnet.org>
 
-  Copyright (C) 2001 - 2005 Pekka Riikonen
+  Copyright (C) 2001 - 2006 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
 #include "silcclient.h"
 #include "client_internal.h"
 
-/******************************************************************************
+/* XXX locking */
 
-                         Client Searching Locally
+/************************ Client Searching Locally **************************/
 
-******************************************************************************/
+/* Finds entry for client by the client's ID. Returns the entry or NULL
+   if the entry was not found. */
+
+SilcClientEntry silc_client_get_client_by_id(SilcClient client,
+                                            SilcClientConnection conn,
+                                            SilcClientID *client_id)
+{
+  SilcIDCacheEntry id_cache;
+  SilcClientEntry client_entry;
 
-/* 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. */
+  if (!client || !conn || !client_id)
+    return NULL;
 
-SilcClientEntry *silc_client_get_clients_local(SilcClient client,
-                                              SilcClientConnection conn,
-                                              const char *nickname,
-                                              const char *format,
-                                              SilcUInt32 *clients_count)
+  SILC_LOG_DEBUG(("Finding client by ID (%s)",
+                 silc_id_render(client_id, SILC_ID_CLIENT)));
+
+  silc_mutex_lock(conn->internal->lock);
+
+  /* Find ID from cache */
+  if (!silc_idcache_find_by_id_one(conn->internal->client_cache, client_id,
+                                  &id_cache))
+    return NULL;
+
+  client_entry = id_cache->context;
+
+  /* Reference */
+  silc_client_ref_client(client, conn, client_entry);
+  silc_mutex_lock(conn->internal->lock);
+
+  SILC_LOG_DEBUG(("Found"));
+
+  return client_entry;
+}
+
+/* Finds clients by nickname from local cache. */
+
+SilcDList silc_client_get_clients_local(SilcClient client,
+                                       SilcClientConnection conn,
+                                       const char *nickname,
+                                       const char *format)
 {
   SilcIDCacheEntry id_cache;
-  SilcIDCacheList list = NULL;
-  SilcClientEntry entry, *clients;
-  int i = 0;
-  SilcBool found = FALSE;
+  SilcList list;
+  SilcDList clients;
+  SilcClientEntry entry;
   char *nicknamec;
 
-  assert(client && conn);
-  if (!nickname)
+  if (!client || !conn || !nickname)
     return NULL;
 
   /* Normalize nickname for search */
   nicknamec = silc_identifier_check(nickname, strlen(nickname),
                                    SILC_STRING_UTF8, 128, NULL);
   if (!nicknamec)
+    silc_free(nicknamec);
     return NULL;
 
-  /* Find ID from cache */
-  if (!silc_idcache_find_by_name(conn->internal->client_cache, nicknamec,
-                                &list)) {
+  clients = silc_dlist_init();
+  if (!clients) {
     silc_free(nicknamec);
     return NULL;
   }
 
-  if (!silc_idcache_list_count(list)) {
-    silc_idcache_list_free(list);
+  silc_mutex_lock(conn->internal->lock);
+
+  /* Find from cache */
+  if (!silc_idcache_find_by_name(conn->internal->client_cache, nicknamec,
+                                &list)) {
+    silc_mutex_unlock(conn->internal->lock);
     silc_free(nicknamec);
+    silc_dlist_uninit(clients);
     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;
+    silc_list_start(list);
+    while ((id_cache = silc_list_get(list))) {
+      silc_client_ref_client(client, conn, entry);
+      silc_dlist_add(clients, 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 (!silc_utf8_strcasecmp(entry->nickname, format)) {
-       if (!silc_idcache_list_next(list, &id_cache)) {
-         break;
-       } else {
-         continue;
-       }
+    /* Check multiple cache entries for exact match */
+    silc_list_start(list);
+    while ((id_cache = silc_list_get(list))) {
+      entry = id_cache->context;
+      if (silc_utf8_strcasecmp(entry->nickname, format)) {
+       silc_client_ref_client(client, conn, entry);
+       silc_dlist_add(clients, entry);
       }
-
-      clients[i++] = id_cache->context;
-      found = TRUE;
-      if (!silc_idcache_list_next(list, &id_cache))
-       break;
     }
   }
 
-  silc_free(nicknamec);
-
-  if (list)
-    silc_idcache_list_free(list);
+  silc_mutex_unlock(conn->internal->lock);
 
-  if (!found) {
-    *clients_count = 0;
-    if (clients)
-      silc_free(clients);
-    return NULL;
-  }
+  silc_dlist_start(clients);
 
+  silc_free(nicknamec);
   return clients;
 }
 
+/********************** Client Resolving from Server ************************/
 
-/******************************************************************************
-
-                        Client Resolving from Server
-
-******************************************************************************/
-
+/* Resolving context */
 typedef struct {
-  SilcClient client;
-  SilcClientConnection conn;
+  SilcDList clients;
   SilcGetClientCallback completion;
   void *context;
-  char *nickname;
-  SilcClientEntry *clients;
-  SilcUInt32 clients_count;
-} *GetClientInternal;
+} *SilcClientGetClientInternal;
 
-/* Completion for IDENTIFY */
+/* Resolving command callback */
 
-SILC_CLIENT_CMD_FUNC(get_client_callback)
-{
-  GetClientInternal i = (GetClientInternal)context;
-  SilcClientEntry *clients;
-  SilcUInt32 clients_count;
-
-  /* Get the clients */
-  clients = silc_client_get_clients_local(i->client, i->conn,
-                                         i->nickname, NULL,
-                                         &clients_count);
-  if (clients) {
-    i->completion(i->client, i->conn, clients, clients_count, i->context);
-    silc_free(clients);
-  } else {
-    i->completion(i->client, i->conn, NULL, 0, i->context);
-  }
-
-  silc_free(i->nickname);
-  silc_free(i);
-}
-
-/* Completion for WHOIS */
-
-SILC_CLIENT_CMD_FUNC(get_client_callback_wc)
+static SilcBool silc_client_get_clients_cb(SilcClient client,
+                                          SilcClientConnection conn,
+                                          SilcCommand command,
+                                          SilcStatus status,
+                                          SilcStatus error,
+                                          void *context,
+                                          va_list ap)
 {
-  GetClientInternal i = (GetClientInternal)context;
-  SilcClientCommandReplyContext cmd = context2;
-  SilcClientID *client_id = NULL;
-  SilcClientEntry client_entry = NULL;
-  unsigned char *id_data;
-  SilcUInt32 len;
-
-  /* Get the client entry just returned from server */
-  id_data = silc_argument_get_arg_type(cmd->args, 2, &len);
-  if (id_data)
-    client_id = silc_id_payload_parse_id(id_data, len, NULL);
-  if (client_id)
-    client_entry = silc_client_get_client_by_id(i->client,
-                                               i->conn, client_id);
-  if (!client_entry) {
-    if (!SILC_STATUS_IS_ERROR(cmd->status) &&
-       cmd->status != SILC_STATUS_OK &&
-       cmd->status != SILC_STATUS_LIST_END) {
-      silc_free(client_id);
-      return;
-    }
+  SilcClientGetClientInternal i = context;
+  SilcClientEntry client_entry;
 
-    i->completion(i->client, i->conn, i->clients, i->clients_count,
-                 i->context);
-    silc_free(client_id);
-    silc_free(i->clients);
-    silc_free(i->nickname);
-    silc_free(i);
-    return;
+  if (error != SILC_STATUS_OK) {
+    SILC_LOG_DEBUG(("Resolving failed: %s", silc_get_status_message(error)));
+    if (i->completion)
+      i->completion(client, conn, error, NULL, i->context);
+    goto out;
   }
 
-  /* Save the client */
-  i->clients = silc_realloc(i->clients,
-                           (sizeof(*i->clients) * (i->clients_count + 1)));
-  i->clients[i->clients_count] = client_entry;
-  i->clients_count++;
+  /* Add the returned client to list */
+  if (i->completion) {
+    client_entry = va_arg(ap, SilcClientEntry);
+    silc_client_ref_client(client, conn, client_entry);
+    silc_dlist_add(i->clients, client_entry);
+  }
 
-  /* Return if more data is expected */
-  if (cmd->status != SILC_STATUS_OK &&
-      cmd->status != SILC_STATUS_LIST_END) {
-    silc_free(client_id);
-    return;
+  if (status == SILC_STATUS_OK || status == SILC_STATUS_LIST_END) {
+    /* Deliver the clients to the caller */
+    if (i->completion) {
+      SILC_LOG_DEBUG(("Resolved %d clients", silc_dlist_count(i->clients)));
+      silc_dlist_start(i->clients);
+      i->completion(client, conn, SILC_STATUS_OK, i->clients, i->context);
+    }
+    goto out;
   }
 
-  i->completion(i->client, i->conn, i->clients, i->clients_count,
-               i->context);
+  return TRUE;
 
-  silc_free(client_id);
-  silc_free(i->clients);
-  silc_free(i->nickname);
+ out:
+  silc_client_list_free(client, conn, i->clients);
   silc_free(i);
+  return FALSE;
 }
 
-/* Our own WHOIS reply processor. */
+/* Resolves client information from server by the client ID. */
 
-SILC_CLIENT_CMD_FUNC(get_client_callback_w)
+void silc_client_get_client_by_id_resolve(SilcClient client,
+                                         SilcClientConnection conn,
+                                         SilcClientID *client_id,
+                                         SilcBuffer attributes,
+                                         SilcGetClientCallback completion,
+                                         void *context)
 {
-  SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
-  SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
-
-  SILC_LOG_DEBUG(("Start"));
-
-  if (!silc_command_get_status(cmd->payload, NULL, NULL)) {
-    if (SILC_STATUS_IS_ERROR(cmd->status))
-      goto out;
-    if (cmd->status == SILC_STATUS_LIST_END)
-      goto out;
-    goto err;
-  }
-
-  /* Save WHOIS info */
-  silc_client_command_reply_whois_save(cmd, cmd->status, FALSE);
+  SilcClientGetClientInternal i;
+  SilcBuffer idp;
 
-  /* Call pending completion for each reply */
-  if (cmd->status != SILC_STATUS_OK &&
-      cmd->status != SILC_STATUS_LIST_END) {
-    if (cmd->callbacks[0].callback)
-      (*cmd->callbacks[0].callback)(cmd->callbacks[0].context, cmd);
-    silc_client_command_reply_free(cmd);
+  if (!client || !conn | !client_id)
     return;
-  }
 
- out:
-  SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_WHOIS);
+  SILC_LOG_DEBUG(("Resolve client by ID (%s)",
+                 silc_id_render(client_id, SILC_ID_CLIENT)));
 
- err:
-  /* If we received notify for invalid ID we'll remove the ID if we
-     have it cached. */
-  if (cmd->error == SILC_STATUS_ERR_NO_SUCH_CLIENT_ID) {
-    SilcClientEntry client_entry;
-    SilcUInt32 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, NULL);
-      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);
-      }
-    }
-  }
+  i = silc_calloc(1, sizeof(*i));
+  if (!i)
+    return;
+  i->completion = completion;
+  i->context = context;
 
-  /* 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);
+  /* Send the command */
+  idp = silc_id_payload_encode(client_id, SILC_ID_CLIENT);
+  silc_client_command_send(client, conn, SILC_COMMAND_WHOIS,
+                          silc_client_get_clients_cb, i,
+                          2, 3, silc_buffer_datalen(attributes),
+                          4, silc_buffer_datalen(idp));
+  silc_buffer_free(idp);
 }
 
 /* Finds client entry or entries by the `nickname' and `server'. The
-   completion callback will be called when the client entries has been found.
-
-   Note: this function is always asynchronous and resolves the client
-   information from the server. Thus, if you already know the client
-   information then use the silc_client_get_client_by_id function to
-   get the client entry since this function may be very slow and should
-   be used only to initially get the client entries. */
-
-void silc_client_get_clients_i(SilcClient client,
-                              SilcClientConnection conn,
-                              SilcCommand command,
-                              const char *nickname,
-                              const char *server,
-                              SilcBuffer attributes,
-                              SilcGetClientCallback completion,
-                              void *context)
+   completion callback will be called when the client entries has been
+   found.  Used internally by the library. */
+
+static SilcUInt16 silc_client_get_clients_i(SilcClient client,
+                                           SilcClientConnection conn,
+                                           SilcCommand command,
+                                           const char *nickname,
+                                           const char *server,
+                                           SilcBuffer attributes,
+                                           SilcGetClientCallback completion,
+                                           void *context)
 {
-  GetClientInternal i;
+  SilcClientGetClientInternal i;
+  char userhost[768 + 1];
   int len;
-  char *userhost = NULL;
 
-  assert(client && conn);
+  SILC_LOG_DEBUG(("Resolve client by %s command",
+                 silc_get_command_name(command)));
 
+  if (!client || !conn)
+    return 0;
   if (!nickname && !attributes)
-    return;
+    return 0;
 
   i = silc_calloc(1, sizeof(*i));
-  i->client = client;
-  i->conn = conn;
-  i->nickname = nickname ? strdup(nickname) : NULL;
+  if (!i)
+    return 0;
+  i->clients = silc_dlist_init();
+  if (!i->clients)
+    return 0;
   i->completion = completion;
   i->context = context;
 
+  memset(userhost, 0, sizeof(userhost));
   if (nickname && server) {
     len = strlen(nickname) + strlen(server) + 3;
-    userhost = silc_calloc(len, sizeof(*userhost));
     silc_strncat(userhost, len, nickname, strlen(nickname));
     silc_strncat(userhost, len, "@", 1);
     silc_strncat(userhost, len, server, strlen(server));
   } else if (nickname) {
-    userhost = silc_memdup(nickname, strlen(nickname));
+    silc_strncat(userhost, sizeof(userhost) - 1, nickname, strlen(nickname));
   }
 
-  /* Register our own command reply for this command */
-  if (command == SILC_COMMAND_IDENTIFY) {
-    silc_client_command_register(client, command, 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, command, conn->cmd_ident,
-                               silc_client_command_get_client_callback,
-                               (void *)i);
-  } else {
-    silc_client_command_register(client, command, NULL, NULL,
-                                silc_client_command_get_client_callback_w, 0,
-                                ++conn->cmd_ident);
-    /* Send the command */
-    silc_client_command_send(client, conn, command, conn->cmd_ident, 2,
-                            1, userhost, userhost ? strlen(userhost) : 0,
-                            3, attributes ? attributes->data : NULL,
-                            attributes ? attributes->len : 0);
-
-    /* Add pending callback */
-    silc_client_command_pending(conn, command, conn->cmd_ident,
-                               silc_client_command_get_client_callback_wc,
-                               (void *)i);
-  }
-  silc_free(userhost);
+  /* Send the command */
+  if (command == SILC_COMMAND_IDENTIFY)
+    return silc_client_command_send(client, conn, SILC_COMMAND_IDENTIFY,
+                                   silc_client_get_clients_cb, i,
+                                   1, 1, userhost, strlen(userhost));
+  return silc_client_command_send(client, conn, SILC_COMMAND_WHOIS,
+                                 silc_client_get_clients_cb, i,
+                                 2, 1, userhost, strlen(userhost),
+                                 3, silc_buffer_datalen(attributes));
 }
 
-void silc_client_get_clients(SilcClient client,
-                            SilcClientConnection conn,
-                            const char *nickname,
-                            const char *server,
-                            SilcGetClientCallback completion,
-                            void *context)
-{
-  silc_client_get_clients_i(client, conn, SILC_COMMAND_IDENTIFY,
-                           nickname, server, NULL,
-                           completion, context);
-}
+/* Get clients from server with IDENTIFY command */
 
-void silc_client_get_clients_whois(SilcClient client,
+SilcUInt16 silc_client_get_clients(SilcClient client,
                                   SilcClientConnection conn,
                                   const char *nickname,
                                   const char *server,
-                                  SilcBuffer attributes,
                                   SilcGetClientCallback completion,
                                   void *context)
 {
-  silc_client_get_clients_i(client, conn, SILC_COMMAND_WHOIS,
-                           nickname, server, attributes,
-                           completion, context);
+  return silc_client_get_clients_i(client, conn, SILC_COMMAND_IDENTIFY,
+                                  nickname, server, NULL,
+                                  completion, context);
 }
 
-/* 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 */
+/* Get clients from server with WHOIS command */
 
-SilcClientEntry silc_idlist_get_client(SilcClient client,
-                                      SilcClientConnection conn,
-                                      const char *nickname,
-                                      const char *format,
-                                      SilcBool query)
+SilcUInt16 silc_client_get_clients_whois(SilcClient client,
+                                        SilcClientConnection conn,
+                                        const char *nickname,
+                                        const char *server,
+                                        SilcBuffer attributes,
+                                        SilcGetClientCallback completion,
+                                        void *context)
 {
-  SilcIDCacheEntry id_cache;
-  SilcIDCacheList list = NULL;
-  SilcClientEntry entry = NULL;
-  char *nicknamec;
-
-  SILC_LOG_DEBUG(("Start"));
-
-  /* Normalize nickname for search */
-  nicknamec = silc_identifier_check(nickname, strlen(nickname),
-                                   SILC_STRING_UTF8, 128, NULL);
-  if (!nicknamec)
-    return NULL;
-
-  /* Find ID from cache */
-  if (!silc_idcache_find_by_name(conn->internal->client_cache,
-                                nicknamec, &list)) {
-  identify:
-
-    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);
-
-      silc_free(nicknamec);
-      return NULL;
-    }
-
-    silc_free(nicknamec);
-    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 (!silc_utf8_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;
-  }
-
-  silc_free(nicknamec);
-
-  if (list)
-    silc_idcache_list_free(list);
-
-  return entry;
+  return silc_client_get_clients_i(client, conn, SILC_COMMAND_WHOIS,
+                                  nickname, server, attributes,
+                                  completion, context);
 }
 
+/* ID list resolving context */
 typedef struct {
-  SilcClient client;
-  SilcClientConnection conn;
-  SilcUInt32 list_count;
-  SilcBuffer client_id_list;
   SilcGetClientCallback completion;
   void *context;
-  int res_count;
+  SilcBuffer client_id_list;
+  SilcUInt32 list_count;
 } *GetClientsByListInternal;
 
-SILC_CLIENT_CMD_FUNC(get_clients_list_callback)
+static SilcBool silc_client_get_clients_list_cb(SilcClient client,
+                                               SilcClientConnection conn,
+                                               SilcCommand command,
+                                               SilcStatus status,
+                                               SilcStatus error,
+                                               void *context,
+                                               va_list ap)
 {
-  GetClientsByListInternal i = (GetClientsByListInternal)context;
-  SilcIDCacheEntry id_cache = NULL;
-  SilcBuffer client_id_list = i->client_id_list;
-  SilcClientEntry *clients = NULL;
-  SilcUInt32 clients_count = 0;
-  SilcBool found = FALSE;
+  GetClientsByListInternal i = context;
+  SilcClientEntry client_entry;
+  SilcDList clients;
+  SilcUInt16 idp_len;
+  SilcID id;
   int c;
 
-  SILC_LOG_DEBUG(("Start"));
-
-  if (i->res_count) {
-    i->res_count--;
-    if (i->res_count)
-      return;
-  }
+  /* Process the list after all replies have been received */
+  if (status != SILC_STATUS_OK && !SILC_STATUS_IS_ERROR(status) &&
+      status != SILC_STATUS_LIST_END)
+    return TRUE;
 
   SILC_LOG_DEBUG(("Resolved all clients"));
 
-  clients = silc_calloc(i->list_count, sizeof(*clients));
+  clients = silc_dlist_init();
+  if (!clients) {
+    status = SILC_STATUS_ERR_RESOURCE_LIMIT;
+    goto out;
+  }
 
   for (c = 0; c < i->list_count; c++) {
-    SilcUInt16 idp_len;
-    SilcClientID *client_id;
-
     /* Get Client ID */
-    SILC_GET16_MSB(idp_len, client_id_list->data + 2);
+    SILC_GET16_MSB(idp_len, i->client_id_list->data + 2);
     idp_len += 4;
-    client_id = silc_id_payload_parse_id(client_id_list->data, idp_len, NULL);
-    if (!client_id) {
-      silc_buffer_pull(client_id_list, idp_len);
-      continue;
+    if (!silc_id_payload_parse_id(i->client_id_list->data, idp_len, &id)) {
+      status = SILC_STATUS_ERR_BAD_CLIENT_ID;
+      goto out;
     }
 
-    /* Get the client entry */
-    if (silc_idcache_find_by_id_one_ext(i->conn->internal->client_cache,
-                                       (void *)client_id,
-                                       NULL, NULL,
-                                       silc_hash_client_id_compare, NULL,
-                                       &id_cache)) {
-      clients[clients_count] = (SilcClientEntry)id_cache->context;
-      clients_count++;
-      found = TRUE;
+    /* Get client entry */
+    client_entry = silc_client_get_client_by_id(client, conn, &id.u.client_id);
+    if (client_entry) {
+      silc_client_ref_client(client, conn, client_entry);
+      silc_dlist_add(clients, client_entry);
     }
 
-    silc_free(client_id);
-    silc_buffer_pull(client_id_list, idp_len);
+    if (!silc_buffer_pull(i->client_id_list, idp_len)) {
+      status = SILC_STATUS_ERR_BAD_CLIENT_ID;
+      goto out;
+    }
   }
 
-  if (found) {
-    i->completion(i->client, i->conn, clients, clients_count, i->context);
-    silc_free(clients);
-  } else {
-    i->completion(i->client, i->conn, NULL, 0, i->context);
-  }
+  silc_dlist_start(clients);
+  status = SILC_STATUS_OK;
+  if (i->completion)
+    i->completion(client, conn, status, clients, i->context);
 
-  if (i->client_id_list)
-    silc_buffer_free(i->client_id_list);
+ out:
+  if (status != SILC_STATUS_OK && i->completion)
+    i->completion(client, conn, status, NULL, i->context);
+  silc_client_list_free(client, conn, clients);
   silc_free(i);
+  return FALSE;
 }
 
 /* Gets client entries by the list of client ID's `client_id_list'. This
@@ -556,131 +383,85 @@ void silc_client_get_clients_by_list(SilcClient client,
                                     SilcGetClientCallback completion,
                                     void *context)
 {
-  SilcIDCacheEntry id_cache = NULL;
-  int i;
+  GetClientsByListInternal in;
+  SilcClientEntry entry;
   unsigned char **res_argv = NULL;
   SilcUInt32 *res_argv_lens = NULL, *res_argv_types = NULL, res_argc = 0;
-  GetClientsByListInternal in;
-  SilcBool wait_res = FALSE;
+  SilcUInt16 idp_len;
+  SilcID id;
+  int i;
 
-  assert(client && conn && client_id_list);
+  SILC_LOG_DEBUG(("Resolve clients from Client ID list"));
 
-  SILC_LOG_DEBUG(("Start"));
+  if (!client || !conn || !client_id_list)
+    return;
 
   in = silc_calloc(1, sizeof(*in));
-  in->client = client;
-  in->conn = conn;
-  in->list_count = list_count;
-  in->client_id_list = silc_buffer_copy(client_id_list);
+  if (!in)
+    return;
   in->completion = completion;
   in->context = context;
+  in->list_count = list_count;
+  in->client_id_list = silc_buffer_copy(client_id_list);
+  if (!in->client_id_list)
+    goto err;
 
   for (i = 0; i < list_count; i++) {
-    SilcUInt16 idp_len;
-    SilcClientID *client_id;
-    SilcClientEntry entry;
-    SilcBool ret;
-
     /* Get Client ID */
     SILC_GET16_MSB(idp_len, client_id_list->data + 2);
     idp_len += 4;
-    client_id = silc_id_payload_parse_id(client_id_list->data, idp_len, NULL);
-    if (!client_id) {
-      silc_buffer_pull(client_id_list, idp_len);
-      continue;
-    }
-
-    /* Check if we have this client cached already. */
-    ret =
-      silc_idcache_find_by_id_one_ext(conn->internal->client_cache,
-                                     (void *)client_id, NULL, NULL,
-                                     silc_hash_client_id_compare, NULL,
-                                     &id_cache);
-
-    /* If we don't have the entry or it has incomplete info, then resolve
-       it from the server. */
-    if (!ret || !((SilcClientEntry)id_cache->context)->nickname) {
-      entry = ret ? (SilcClientEntry)id_cache->context : NULL;
-
-      if (entry) {
-       if (entry->status & SILC_CLIENT_STATUS_RESOLVING) {
-         /* Attach to this resolving and wait until it finishes */
-         silc_client_command_pending(
-                           conn, SILC_COMMAND_NONE,
-                           entry->resolve_cmd_ident,
-                           silc_client_command_get_clients_list_callback,
-                           (void *)in);
-         wait_res = TRUE;
-         in->res_count++;
-
-         silc_free(client_id);
-         silc_buffer_pull(client_id_list, idp_len);
-         continue;
-       }
-
-       entry->status |= SILC_CLIENT_STATUS_RESOLVING;
-       entry->resolve_cmd_ident = conn->cmd_ident + 1;
+    if (!silc_id_payload_parse_id(client_id_list->data, idp_len, &id))
+      goto err;
+
+    /* Check if we have this client cached already.  If we don't have the
+       entry or it has incomplete info, then resolve it from the server. */
+    entry = silc_client_get_client_by_id(client, conn, &id.u.client_id);
+    if (!entry || !entry->nickname || !entry->username || !entry->realname) {
+      if (!res_argv) {
+       res_argv = silc_calloc(list_count, sizeof(*res_argv));
+       res_argv_lens = silc_calloc(list_count, sizeof(*res_argv_lens));
+       res_argv_types = silc_calloc(list_count, sizeof(*res_argv_types));
+       if (!res_argv || !res_argv_lens || !res_argv_types)
+         goto err;
       }
 
-      /* No we don't have it, query it from the server. Assemble argument
-        table that will be sent for the IDENTIFY command later. */
-      res_argv = silc_realloc(res_argv, sizeof(*res_argv) *
-                             (res_argc + 1));
-      res_argv_lens = silc_realloc(res_argv_lens, sizeof(*res_argv_lens) *
-                                  (res_argc + 1));
-      res_argv_types = silc_realloc(res_argv_types, sizeof(*res_argv_types) *
-                                   (res_argc + 1));
       res_argv[res_argc] = client_id_list->data;
       res_argv_lens[res_argc] = idp_len;
       res_argv_types[res_argc] = res_argc + 5;
       res_argc++;
     }
 
-    silc_free(client_id);
-    silc_buffer_pull(client_id_list, idp_len);
+    if (!silc_buffer_pull(client_id_list, idp_len))
+      goto err;
   }
+  silc_buffer_start(client_id_list);
 
-  silc_buffer_push(client_id_list, client_id_list->data -
-                  client_id_list->head);
-
-  /* Query the client information from server if the list included clients
-     that we don't know about. */
+  /* Query the unknown client information from server */
   if (res_argc) {
-    SilcBuffer res_cmd;
-
-    /* Send the IDENTIFY command to server */
-    res_cmd = silc_command_payload_encode(SILC_COMMAND_IDENTIFY,
-                                         res_argc, res_argv, res_argv_lens,
-                                         res_argv_types, ++conn->cmd_ident);
-    silc_client_packet_send(client, conn->sock, SILC_PACKET_COMMAND,
-                           NULL, 0, NULL, NULL, res_cmd->data, res_cmd->len,
-                           TRUE);
-
-    /* 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_command_get_clients_list_callback,
-                               (void *)in);
-    in->res_count++;
-
-    silc_buffer_free(res_cmd);
+    silc_client_command_send_argv(client, conn, SILC_COMMAND_WHOIS,
+                                 silc_client_get_clients_list_cb,
+                                 in, res_argc, res_argv, res_argv_lens,
+                                 res_argv_types);
     silc_free(res_argv);
     silc_free(res_argv_lens);
     silc_free(res_argv_types);
     return;
   }
 
-  if (wait_res)
-    return;
-
   /* We have the clients in cache, get them and call the completion */
-  silc_client_command_get_clients_list_callback((void *)in, NULL);
+  silc_client_get_clients_list_cb(client, conn, SILC_COMMAND_WHOIS,
+                                 SILC_STATUS_OK, SILC_STATUS_OK, in, NULL);
+  return;
+
+ err:
+  silc_buffer_free(in->client_id_list);
+  silc_free(in);
+  silc_free(res_argv);
+  silc_free(res_argv_lens);
+  silc_free(res_argv_types);
 }
 
+#if 0
 typedef struct {
   SilcClient client;
   SilcClientConnection conn;
@@ -849,152 +630,57 @@ void silc_client_get_clients_by_channel(SilcClient client,
   /* We have the clients in cache, get them and call the completion */
   silc_client_command_get_clients_by_channel_cb((void *)in, NULL);
 }
+#endif /* 0 */
 
-/* Finds entry for client by the client's ID. Returns the entry or NULL
-   if the entry was not found. */
-
-SilcClientEntry silc_client_get_client_by_id(SilcClient client,
-                                            SilcClientConnection conn,
-                                            SilcClientID *client_id)
-{
-  SilcIDCacheEntry id_cache;
-
-  assert(client && conn);
-  if (!client_id)
-    return NULL;
-
-  SILC_LOG_DEBUG(("Finding client by ID (%s)",
-                 silc_id_render(client_id, SILC_ID_CLIENT)));
-
-  /* Find ID from cache */
-  if (!silc_idcache_find_by_id_one_ext(conn->internal->client_cache,
-                                      (void *)client_id, NULL, NULL,
-                                      silc_hash_client_id_compare, NULL,
-                                      &id_cache))
-    return NULL;
-
-  SILC_LOG_DEBUG(("Found"));
-
-  return (SilcClientEntry)id_cache->context;
-}
-
-typedef struct {
-  SilcClient client;
-  SilcClientConnection conn;
-  SilcClientID *client_id;
-  SilcGetClientCallback completion;
-  void *context;
-} *GetClientByIDInternal;
-
-SILC_CLIENT_CMD_FUNC(get_client_by_id_callback)
-{
-  GetClientByIDInternal i = (GetClientByIDInternal)context;
-  SilcClientEntry entry;
-
-  /* Get the client */
-  entry = silc_client_get_client_by_id(i->client, i->conn, i->client_id);
-  if (entry) {
-    if (i->completion)
-      i->completion(i->client, i->conn, &entry, 1, i->context);
-  } else {
-    if (i->completion)
-      i->completion(i->client, i->conn, NULL, 0, i->context);
-  }
-
-  silc_free(i->client_id);
-  silc_free(i);
-}
-
-/* Same as above but will always resolve the information from the server.
-   Use this only if you know that you don't have the entry and the only
-   thing you know about the client is its ID. */
-
-void silc_client_get_client_by_id_resolve(SilcClient client,
-                                         SilcClientConnection conn,
-                                         SilcClientID *client_id,
-                                         SilcBuffer attributes,
-                                         SilcGetClientCallback completion,
-                                         void *context)
-{
-  SilcBuffer idp;
-  GetClientByIDInternal i = silc_calloc(1, sizeof(*i));
-
-  assert(client && conn && client_id);
-
-  SILC_LOG_DEBUG(("Start"));
-
-  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,
-                          2, 3, attributes ? attributes->data : NULL,
-                          attributes ? attributes->len : 0,
-                          4, 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_get_client_by_id_callback,
-                             (void *)i);
-}
-
-
-/******************************************************************************
-
-                Client, Channel and Server entry manipulation
-
-******************************************************************************/
 
+/************************** Client Entry Routines ***************************/
 
 /* Creates new client entry and adds it to the ID cache. Returns pointer
    to the new entry. */
 
-SilcClientEntry
-silc_client_add_client(SilcClient client, SilcClientConnection conn,
-                      char *nickname, char *username,
-                      char *userinfo, SilcClientID *id, SilcUInt32 mode)
+SilcClientEntry silc_client_add_client(SilcClient client,
+                                      SilcClientConnection conn,
+                                      char *nickname, char *username,
+                                      char *userinfo, SilcClientID *id,
+                                      SilcUInt32 mode)
 {
   SilcClientEntry client_entry;
   char *nick = NULL;
 
-  SILC_LOG_DEBUG(("Start"));
+  SILC_LOG_DEBUG(("Adding new client entry"));
 
   /* Save the client infos */
   client_entry = silc_calloc(1, sizeof(*client_entry));
-  client_entry->id = id;
-  client_entry->valid = TRUE;
-  silc_parse_userfqdn(nickname, &nick, &client_entry->server);
-  silc_parse_userfqdn(username, &client_entry->username,
-                     &client_entry->hostname);
-  if (userinfo)
-    client_entry->realname = strdup(userinfo);
+  if (!client_entry)
+    return NULL;
+
+  client_entry->id = *id;
+  client_entry->internal.valid = TRUE;
   client_entry->mode = mode;
-  if (nick)
-    client_entry->nickname = strdup(nick);
+  client_entry->realname = userinfo ? strdup(userinfo) : NULL;
+  silc_parse_userfqdn(nickname, client_entry->nickname,
+                     sizeof(client_entry->nickname),
+                     client_entry->server,
+                     sizeof(client_entry->server));
+  silc_parse_userfqdn(username, client_entry->username,
+                     sizeof(client_entry->username),
+                     client_entry->hostname,
+                     sizeof(client_entry->hostname));
   client_entry->channels = silc_hash_table_alloc(1, silc_hash_ptr, NULL, NULL,
                                                 NULL, NULL, NULL, TRUE);
+  if (!client_entry->channels) {
+    silc_free(client_entry->realname);
+    silc_free(client_entry);
+    return NULL;
+  }
 
   /* Normalize nickname */
-  if (client_entry->nickname) {
-    silc_free(nick);
+  if (client_entry->nickname[0]) {
     nick = silc_identifier_check(client_entry->nickname,
                                 strlen(client_entry->nickname),
                                 SILC_STRING_UTF8, 128, NULL);
     if (!nick) {
-      silc_free(client_entry->nickname);
-      silc_free(client_entry->username);
-      silc_free(client_entry->hostname);
-      silc_free(client_entry->server);
+      silc_free(client_entry->realname);
       silc_hash_table_free(client_entry->channels);
       silc_free(client_entry);
       return NULL;
@@ -1004,19 +690,18 @@ silc_client_add_client(SilcClient client, SilcClientConnection conn,
   /* Format the nickname */
   silc_client_nickname_format(client, conn, client_entry);
 
-  /* Add client to cache, the non-formatted nickname is saved to cache */
-  if (!silc_idcache_add(conn->internal->client_cache, nick, client_entry->id,
-                       (void *)client_entry, 0, NULL)) {
+  /* Add client to cache, the normalized nickname is saved to cache */
+  if (!silc_idcache_add(conn->internal->client_cache, nick,
+                       &client_entry->id, client_entry)) {
     silc_free(nick);
-    silc_free(client_entry->nickname);
-    silc_free(client_entry->username);
-    silc_free(client_entry->hostname);
-    silc_free(client_entry->server);
+    silc_free(client_entry->realname);
     silc_hash_table_free(client_entry->channels);
     silc_free(client_entry);
     return NULL;
   }
 
+  client_entry->nickname_normalized = nick;
+
   return client_entry;
 }
 
@@ -1032,24 +717,22 @@ void silc_client_update_client(SilcClient client,
 {
   char *nick = NULL;
 
-  SILC_LOG_DEBUG(("Start"));
+  SILC_LOG_DEBUG(("Update client entry"));
 
-  if ((!client_entry->username || !client_entry->hostname) && username) {
-    silc_free(client_entry->username);
-    silc_free(client_entry->hostname);
-    client_entry->username = NULL;
-    client_entry->hostname = NULL;
-    silc_parse_userfqdn(username, &client_entry->username,
-                       &client_entry->hostname);
-  }
   if (!client_entry->realname && userinfo)
     client_entry->realname = strdup(userinfo);
-  if (!client_entry->nickname && nickname) {
-    silc_parse_userfqdn(nickname, &nick, &client_entry->server);
-    client_entry->nickname = strdup(nick);
+  if ((!client_entry->username[0] || !client_entry->hostname[0]) && username)
+    silc_parse_userfqdn(username, client_entry->username,
+                       sizeof(client_entry->username),
+                       client_entry->hostname,
+                       sizeof(client_entry->username));
+  if (!client_entry->nickname[0] && nickname) {
+    silc_parse_userfqdn(nickname, client_entry->nickname,
+                       sizeof(client_entry->nickname),
+                       client_entry->server,
+                       sizeof(client_entry->server));
 
     /* Normalize nickname */
-    silc_free(nick);
     nick = silc_identifier_check(client_entry->nickname,
                                 strlen(client_entry->nickname),
                                 SILC_STRING_UTF8, 128, NULL);
@@ -1058,15 +741,14 @@ void silc_client_update_client(SilcClient client,
 
     /* Format nickname */
     silc_client_nickname_format(client, conn, client_entry);
-  }
-  client_entry->mode = mode;
 
-  if (nick) {
     /* Remove the old cache entry and create a new one */
-    silc_idcache_del_by_context(conn->internal->client_cache, client_entry);
-    silc_idcache_add(conn->internal->client_cache, nick, client_entry->id,
-                    client_entry, 0, NULL);
+    silc_idcache_del_by_context(conn->internal->client_cache, client_entry,
+                               NULL);
+    silc_idcache_add(conn->internal->client_cache, nick, &client_entry->id,
+                    client_entry);
   }
+  client_entry->mode = mode;
 }
 
 /* Deletes the client entry and frees all memory. */
@@ -1077,35 +759,31 @@ void silc_client_del_client_entry(SilcClient client,
 {
   SILC_LOG_DEBUG(("Start"));
 
-  silc_free(client_entry->nickname);
-  silc_free(client_entry->username);
   silc_free(client_entry->realname);
-  silc_free(client_entry->hostname);
-  silc_free(client_entry->server);
-  silc_free(client_entry->id);
-  silc_free(client_entry->fingerprint);
   if (client_entry->public_key)
     silc_pkcs_public_key_free(client_entry->public_key);
   silc_hash_table_free(client_entry->channels);
-  if (client_entry->send_key)
-    silc_cipher_free(client_entry->send_key);
-  if (client_entry->receive_key)
-    silc_cipher_free(client_entry->receive_key);
-  silc_free(client_entry->key);
+  if (client_entry->internal.send_key)
+    silc_cipher_free(client_entry->internal.send_key);
+  if (client_entry->internal.receive_key)
+    silc_cipher_free(client_entry->internal.receive_key);
+  silc_free(client_entry->internal.key);
+#if 0
   silc_client_ftp_session_free_client(conn, client_entry);
-  if (client_entry->ke)
+  if (client_entry->internal->ke)
     silc_client_abort_key_agreement(client, conn, client_entry);
+#endif /* 0 */
   silc_free(client_entry);
 }
 
 /* Removes client from the cache by the client entry. */
 
 SilcBool silc_client_del_client(SilcClient client, SilcClientConnection conn,
-                           SilcClientEntry client_entry)
+                               SilcClientEntry client_entry)
 {
   SilcBool ret = silc_idcache_del_by_context(conn->internal->client_cache,
-                                        client_entry);
-
+                                            client_entry, NULL);
+#if 0
   if (ret) {
     /* Remove from channels */
     silc_client_remove_from_channels(client, conn, client_entry);
@@ -1113,10 +791,47 @@ SilcBool silc_client_del_client(SilcClient client, SilcClientConnection conn,
     /* Free the client entry data */
     silc_client_del_client_entry(client, conn, client_entry);
   }
+#endif
 
   return ret;
 }
 
+/* Take reference of client entry */
+
+void silc_client_ref_client(SilcClient client, SilcClientConnection conn,
+                           SilcClientEntry client_entry)
+{
+  silc_atomic_add_int8(&client_entry->internal.refcnt, 1);
+}
+
+/* Release reference of client entry */
+
+void silc_client_unref_client(SilcClient client, SilcClientConnection conn,
+                             SilcClientEntry client_entry)
+{
+  if (silc_atomic_sub_int8(&client_entry->internal.refcnt, 1) == 0) {
+    silc_client_del_client(client, conn, client_entry);
+    return;
+  }
+}
+
+/* Free client entry list */
+
+void silc_client_list_free(SilcClient client, SilcClientConnection conn,
+                          SilcDList client_list)
+{
+  SilcClientEntry client_entry;
+
+  silc_dlist_start(client_list);
+  while ((client_entry = silc_dlist_get(client_list)))
+    silc_client_unref_client(client, conn, client_entry);
+
+  silc_dlist_uninit(client_list);
+}
+
+
+/************************* Channel Entry Routines ***************************/
+
 /* Add new channel entry to the ID Cache */
 
 SilcChannelEntry silc_client_add_channel(SilcClient client,
@@ -1149,7 +864,7 @@ SilcChannelEntry silc_client_add_channel(SilcClient client,
 
   /* Put it to the ID cache */
   if (!silc_idcache_add(conn->internal->channel_cache, channel_namec,
-                       (void *)channel->id, (void *)channel, 0, NULL)) {
+                       (void *)channel->id, (void *)channel)) {
     silc_free(channel_namec);
     silc_free(channel->channel_name);
     silc_hash_table_free(channel->user_list);
@@ -1179,10 +894,10 @@ static void silc_client_del_channel_foreach(void *key, void *context,
 /* Removes channel from the cache by the channel entry. */
 
 SilcBool silc_client_del_channel(SilcClient client, SilcClientConnection conn,
-                            SilcChannelEntry channel)
+                                SilcChannelEntry channel)
 {
   SilcBool ret = silc_idcache_del_by_context(conn->internal->channel_cache,
-                                        channel);
+                                            channel, NULL);
 
   SILC_LOG_DEBUG(("Start"));
 
@@ -1227,9 +942,9 @@ SilcBool silc_client_del_channel(SilcClient client, SilcClientConnection conn,
    if the ID could not be changed. */
 
 SilcBool silc_client_replace_channel_id(SilcClient client,
-                                   SilcClientConnection conn,
-                                   SilcChannelEntry channel,
-                                   SilcChannelID *new_id)
+                                       SilcClientConnection conn,
+                                       SilcChannelEntry channel,
+                                       SilcChannelID *new_id)
 {
   char *channel_namec;
 
@@ -1241,7 +956,7 @@ SilcBool silc_client_replace_channel_id(SilcClient client,
   SILC_LOG_DEBUG(("New Channel ID id(%s)",
                  silc_id_render(new_id, SILC_ID_CHANNEL)));
 
-  silc_idcache_del_by_id(conn->internal->channel_cache, channel->id);
+  silc_idcache_del_by_id(conn->internal->channel_cache, channel->id, NULL);
   silc_free(channel->id);
   channel->id = new_id;
 
@@ -1253,8 +968,7 @@ SilcBool silc_client_replace_channel_id(SilcClient client,
     return FALSE;
 
   return silc_idcache_add(conn->internal->channel_cache, channel_namec,
-                         (void *)channel->id, (void *)channel, 0, NULL);
-
+                         channel->id, channel) != NULL;
 }
 
 /* Finds entry for channel by the channel name. Returns the entry or NULL
@@ -1323,6 +1037,7 @@ SilcChannelEntry silc_client_get_channel_by_id(SilcClient client,
   return entry;
 }
 
+#if 0
 typedef struct {
   SilcClient client;
   SilcClientConnection conn;
@@ -1446,6 +1161,7 @@ void silc_client_get_channel_by_id_resolve(SilcClient client,
                              silc_client_command_get_channel_by_id_callback,
                              (void *)i);
 }
+#endif /* 0 */
 
 /* Finds entry for server by the server name. */
 
@@ -1543,7 +1259,7 @@ SilcServerEntry silc_client_add_server(SilcClient client,
 
   /* Add server to cache */
   if (!silc_idcache_add(conn->internal->server_cache, server_namec,
-                       server_entry->server_id, server_entry, 0, NULL)) {
+                       server_entry->server_id, server_entry)) {
     silc_free(server_namec);
     silc_free(server_entry->server_id);
     silc_free(server_entry->server_name);
@@ -1560,7 +1276,8 @@ SilcServerEntry silc_client_add_server(SilcClient client,
 SilcBool silc_client_del_server(SilcClient client, SilcClientConnection conn,
                            SilcServerEntry server)
 {
-  SilcBool ret = silc_idcache_del_by_context(conn->internal->server_cache, server);
+  SilcBool ret = silc_idcache_del_by_context(conn->internal->server_cache,
+                                            server, NULL);
   silc_free(server->server_name);
   silc_free(server->server_info);
   silc_free(server->server_id);
@@ -1584,7 +1301,8 @@ void silc_client_update_server(SilcClient client,
       (!server_entry->server_name ||
        !silc_utf8_strcasecmp(server_entry->server_name, server_name))) {
 
-    silc_idcache_del_by_context(conn->internal->server_cache, server_entry);
+    silc_idcache_del_by_context(conn->internal->server_cache,
+                               server_entry, NULL);
     silc_free(server_entry->server_name);
     server_entry->server_name = strdup(server_name);
 
@@ -1596,8 +1314,7 @@ void silc_client_update_server(SilcClient client,
        return;
 
       silc_idcache_add(conn->internal->server_cache, server_namec,
-                      server_entry->server_id,
-                      server_entry, 0, NULL);
+                      server_entry->server_id, server_entry);
     }
   }
 
@@ -1619,12 +1336,11 @@ void silc_client_nickname_format(SilcClient client,
                                 SilcClientEntry client_entry)
 {
   char *cp;
-  char *newnick = NULL;
+  char newnick[128 + 1];
   int i, off = 0, len;
   SilcBool freebase;
-  SilcClientEntry *clients;
-  SilcUInt32 clients_count = 0;
-  SilcClientEntry unformatted = NULL;
+  SilcDList clients;
+  SilcClientEntry entry, unformatted = NULL;
 
   SILC_LOG_DEBUG(("Start"));
 
@@ -1638,37 +1354,31 @@ void silc_client_nickname_format(SilcClient client,
      if there aren't any clients with same nickname unless the application
      is forcing us to do so. */
   clients = silc_client_get_clients_local(client, conn,
-                                         client_entry->nickname, NULL,
-                                         &clients_count);
+                                         client_entry->nickname, NULL);
   if (!clients && !client->internal->params->nickname_force_format)
     return;
 
   len = 0;
   freebase = TRUE;
-  for (i = 0; i < clients_count; i++) {
-    if (clients[i]->valid && clients[i] != client_entry)
+  while ((entry = silc_dlist_get(clients))) {
+    if (entry->internal.valid && entry != client_entry)
       len++;
-    if (clients[i]->valid && clients[i] != client_entry &&
-       silc_utf8_strcasecmp(clients[i]->nickname, client_entry->nickname))
+    if (entry->internal.valid && entry != client_entry &&
+       silc_utf8_strcasecmp(entry->nickname, client_entry->nickname)) {
       freebase = FALSE;
+      unformatted = entry;
+    }
   }
   if (!len || freebase)
     return;
 
-  if (clients_count == 1)
-    unformatted = clients[0];
-  else
-    for (i = 0; i < clients_count; i++)
-      if (silc_utf8_strncasecmp(clients[i]->nickname, client_entry->nickname,
-                               strlen(clients[i]->nickname)))
-       unformatted = clients[i];
-
   /* If we are changing nickname of our local entry we'll enforce
      that we will always get the unformatted nickname.  Give our
      format number to the one that is not formatted now. */
   if (unformatted && client_entry == conn->local_entry)
     client_entry = unformatted;
 
+  memset(newnick, 0, sizeof(newnick));
   cp = client->internal->params->nickname_format;
   while (*cp) {
     if (*cp == '%') {
@@ -1679,31 +1389,28 @@ void silc_client_nickname_format(SilcClient client,
     switch(*cp) {
     case 'n':
       /* Nickname */
-      if (!client_entry->nickname)
+      if (!client_entry->nickname[0])
        break;
       len = strlen(client_entry->nickname);
-      newnick = silc_realloc(newnick, sizeof(*newnick) * (off + len));
       memcpy(&newnick[off], client_entry->nickname, len);
       off += len;
       break;
     case 'h':
       /* Stripped hostname */
-      if (!client_entry->hostname)
+      if (!client_entry->hostname[0])
        break;
       len = strcspn(client_entry->hostname, ".");
       i = strcspn(client_entry->hostname, "-");
       if (i < len)
         len = i;
-      newnick = silc_realloc(newnick, sizeof(*newnick) * (off + len));
       memcpy(&newnick[off], client_entry->hostname, len);
       off += len;
       break;
     case 'H':
       /* Full hostname */
-      if (!client_entry->hostname)
+      if (!client_entry->hostname[0])
        break;
       len = strlen(client_entry->hostname);
-      newnick = silc_realloc(newnick, sizeof(*newnick) * (off + len));
       memcpy(&newnick[off], client_entry->hostname, len);
       off += len;
       break;
@@ -1712,7 +1419,6 @@ void silc_client_nickname_format(SilcClient client,
       if (!client_entry->server)
        break;
       len = strcspn(client_entry->server, ".");
-      newnick = silc_realloc(newnick, sizeof(*newnick) * (off + len));
       memcpy(&newnick[off], client_entry->server, len);
       off += len;
       break;
@@ -1721,7 +1427,6 @@ void silc_client_nickname_format(SilcClient client,
       if (!client_entry->server)
        break;
       len = strlen(client_entry->server);
-      newnick = silc_realloc(newnick, sizeof(*newnick) * (off + len));
       memcpy(&newnick[off], client_entry->server, len);
       off += len;
       break;
@@ -1731,15 +1436,16 @@ void silc_client_nickname_format(SilcClient client,
        char tmp[6];
        int num, max = 1;
 
-       if (clients_count == 1)
+       if (silc_dlist_count(clients) == 1)
          break;
 
-       for (i = 0; i < clients_count; i++) {
-         if (!silc_utf8_strncasecmp(clients[i]->nickname, newnick, off))
+       silc_dlist_start(clients);
+       while ((entry = silc_dlist_get(clients))) {
+         if (!silc_utf8_strncasecmp(entry->nickname, newnick, off))
            continue;
-         if (strlen(clients[i]->nickname) <= off)
+         if (strlen(entry->nickname) <= off)
            continue;
-         num = atoi(&clients[i]->nickname[off]);
+         num = atoi(&entry->nickname[off]);
          if (num > max)
            max = num;
        }
@@ -1747,14 +1453,12 @@ void silc_client_nickname_format(SilcClient client,
        memset(tmp, 0, sizeof(tmp));
        snprintf(tmp, sizeof(tmp) - 1, "%d", ++max);
        len = strlen(tmp);
-       newnick = silc_realloc(newnick, sizeof(*newnick) * (off + len));
        memcpy(&newnick[off], tmp, len);
        off += len;
       }
       break;
     default:
       /* Some other character in the string */
-      newnick = silc_realloc(newnick, sizeof(*newnick) * (off + 1));
       memcpy(&newnick[off], cp, 1);
       off++;
       break;
@@ -1763,10 +1467,7 @@ void silc_client_nickname_format(SilcClient client,
     cp++;
   }
 
-  newnick = silc_realloc(newnick, sizeof(*newnick) * (off + 1));
   newnick[off] = 0;
-
-  silc_free(client_entry->nickname);
-  client_entry->nickname = newnick;
-  silc_free(clients);
+  memcpy(client_entry->nickname, newnick, strlen(newnick));
+  silc_dlist_uninit(clients);
 }
similarity index 73%
rename from lib/silcclient/idlist.h
rename to lib/silcclient/client_entry.h
index 7338486d6f12b06c4db84c7c30217f87e66e8198..e82ece6b41eb5fd5cb64796c2c660d28e83b07d7 100644 (file)
@@ -1,10 +1,10 @@
 /*
 
-  idlist.h 
+  client_entry.h
 
   Author: Pekka Riikonen <priikone@silcnet.org>
 
-  Copyright (C) 2001 - 2002 Pekka Riikonen
+  Copyright (C) 2006 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
 
 */
 
-#ifndef IDLIST_H
-#define IDLIST_H
+#ifndef CLIENT_ENTRY_H
+#define CLIENT_ENTRY_H
 
-/* Prototypes. These are used only by the library. Application should not
-   call these directly. */
-
-SilcClientEntry
-silc_client_add_client(SilcClient client, SilcClientConnection conn,
-                      char *nickname, char *username, 
-                      char *userinfo, SilcClientID *id, SilcUInt32 mode);
+SilcClientEntry silc_client_add_client(SilcClient client,
+                                      SilcClientConnection conn,
+                                      char *nickname, char *username,
+                                      char *userinfo, SilcClientID *id,
+                                      SilcUInt32 mode);
 void silc_client_update_client(SilcClient client,
                               SilcClientConnection conn,
                               SilcClientEntry client_entry,
@@ -34,18 +32,18 @@ void silc_client_update_client(SilcClient client,
                               const char *username,
                               const char *userinfo,
                               SilcUInt32 mode);
-void silc_client_del_client_entry(SilcClient client, 
+void silc_client_del_client_entry(SilcClient client,
                                  SilcClientConnection conn,
                                  SilcClientEntry client_entry);
 SilcClientEntry silc_idlist_get_client(SilcClient client,
                                       SilcClientConnection conn,
                                       const char *nickname,
                                       const char *format,
-                                      SilcBool query);
+                                      bool query);
 SilcChannelEntry silc_client_add_channel(SilcClient client,
                                         SilcClientConnection conn,
                                         const char *channel_name,
-                                        SilcUInt32 mode, 
+                                        SilcUInt32 mode,
                                         SilcChannelID *channel_id);
 SilcServerEntry silc_client_add_server(SilcClient client,
                                       SilcClientConnection conn,
@@ -57,12 +55,12 @@ void silc_client_update_server(SilcClient client,
                               SilcServerEntry server_entry,
                               const char *server_name,
                               const char *server_info);
-SilcBool silc_client_replace_channel_id(SilcClient client,
+bool silc_client_replace_channel_id(SilcClient client,
                                    SilcClientConnection conn,
                                    SilcChannelEntry channel,
                                    SilcChannelID *new_id);
-void silc_client_nickname_format(SilcClient client, 
+void silc_client_nickname_format(SilcClient client,
                                 SilcClientConnection conn,
                                 SilcClientEntry client_entry);
 
-#endif
+#endif /* CLIENT_ENTRY_H */
index f676873e85289d290229c7e2296d34447b2b44c8..0016a73241a5fc4fe3d3895b76248cd6921b8da8 100644 (file)
@@ -4,7 +4,7 @@
 
   Author: Pekka Riikonen <priikone@silcnet.org>
 
-  Copyright (C) 1997 - 2004 Pekka Riikonen
+  Copyright (C) 1997 - 2006 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
 #ifndef CLIENT_INTERNAL_H
 #define CLIENT_INTERNAL_H
 
+#include "command.h"
+#include "command_reply.h"
+#include "client_connect.h"
+#include "client_register.h"
+#include "client_entry.h"
+#include "client_prvmsg.h"
+
 /* Context to hold the connection authentication request callbacks that
    will be called when the server has replied back to our request about
    current authentication method in the session. */
@@ -50,7 +57,6 @@ typedef struct {
   int sock;
   char *host;
   int port;
-  int tries;
   void *context;
 } SilcClientInternalConnectContext;
 
@@ -72,48 +78,79 @@ struct SilcClientAwayStruct {
   struct SilcClientAwayStruct *next;
 };
 
+/* Command and command reply context used to hold registered commands
+   in the SILC client. */
+typedef struct SilcClientCommandStruct {
+  struct SilcClientCommandStruct *next;
+  SilcCommand cmd;                   /* Command type */
+  SilcFSMStateCallback command;              /* Command function */
+  SilcFSMStateCallback reply;        /* Command reply callback */
+  char *name;                        /* Name of the command (optional) */
+  SilcUInt8 max_args;                /* Maximum arguments (optional)  */
+} *SilcClientCommand;
+
+/* Command reply callback structure */
+typedef struct SilcClientCommandReplyCallbackStruct  {
+  struct SilcClientCommandReplyCallbackStruct *next;
+  SilcClientCommandReply reply;              /* Command reply callback */
+  void *context;                     /* Command reply context */
+  unsigned int do_not_call     : 1;   /* Set to not call the callback */
+} *SilcClientCommandReplyCallback;
+
+/* Command context given as argument to command state functions.  This same
+   context is used when calling, sending and procesing command and command
+   reply. */
+typedef struct SilcClientCommandContextStruct {
+  struct SilcClientCommandContextStruct *next;
+  SilcClientConnection conn;          /* Connection */
+  SilcFSMThreadStruct thread;        /* FSM thread for command call */
+
+  SilcCommand cmd;                   /* Command */
+  SilcUInt16 cmd_ident;                      /* Command identifier */
+  SilcUInt32 argc;                   /* Number of arguments */
+  unsigned char **argv;                      /* Arguments, may be NULL */
+  SilcUInt32 *argv_lens;             /* Argument lengths, may be NULL */
+  SilcUInt32 *argv_types;            /* Argument types, may be NULL */
+
+  SilcList reply_callbacks;          /* Command reply callbacks */
+  SilcStatus status;                 /* Current command reply status */
+  SilcStatus error;                  /* Current command reply error */
+
+  void *context;                     /* Context for free use */
+  unsigned int processed     : 1;     /* Set when reply was processed  */
+  unsigned int called        : 1;     /* Set when called by application */
+  unsigned int verbose       : 1;     /* Verbose with 'say' client operation */
+  unsigned int resolved      : 1;     /* Set when resolving something */
+} *SilcClientCommandContext;
+
 /* 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;
-  SilcUInt32 conns_count;
+  SilcFSMStruct fsm;                    /* Client's FSM */
+  SilcFSMSemaStruct wait_event;                 /* Event signaller */
+  SilcClientOperations *ops;            /* Client operations */
+  SilcClientParams *params;             /* Client parameters */
+  SilcPacketEngine packet_engine;        /* Packet engine */
+  SilcMutex lock;                       /* Client lock */
 
-  /* 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;
-  SilcUInt32 sockets_count;
+  /* List of connections in client. All the connection data is saved here. */
+  SilcDList conns;
 
   /* Registered commands */
   SilcList commands;
 
   /* Generic cipher and hash objects. */
-  SilcCipher none_cipher;
   SilcHmac md5hmac;
   SilcHmac sha1hmac;
 
   /* Client version. Used to compare to remote host's version strings. */
   char *silc_client_version;
+
+  /* Events */
+  unsigned int run_callback    : 1;     /* Call running callback */
 };
 
 /* Internal context for conn->internal in SilcClientConnection. */
 struct SilcClientConnectionInternalStruct {
-  /* Keys and stuff negotiated in the SKE protocol */
-  SilcCipher send_key;
-  SilcCipher receive_key;
-  SilcHmac hmac_send;
-  SilcHmac hmac_receive;
-  SilcHash hash;
-  SilcUInt32 psn_send;
-  SilcUInt32 psn_receive;
-
   /* Client ID and Channel ID cache. Messages transmitted in SILC network
      are done using different unique ID's. These are the cache for
      thoses ID's used in the communication. */
@@ -122,7 +159,7 @@ struct SilcClientConnectionInternalStruct {
   SilcIDCache server_cache;
 
   /* Pending command queue for this connection */
-  SilcDList pending_commands;
+  SilcList pending_commands;
 
   /* Requested pings. */
   SilcClientPing *ping;
@@ -131,9 +168,6 @@ struct SilcClientConnectionInternalStruct {
   /* Set away message */
   SilcClientAway *away;
 
-  /* Re-key context */
-  SilcClientRekey rekey;
-
   /* Authentication request context. */
   SilcClientConnAuthRequest connauth;
 
@@ -145,10 +179,68 @@ struct SilcClientConnectionInternalStruct {
   /* Requested Attributes */
   SilcHashTable attrs;
 
-  /* Connection parameters */
-  SilcClientConnectionParams params;
+  SilcFSMStruct fsm;                    /* Connection FSM */
+  SilcFSMThreadStruct packet_thread;     /* FSM thread for packet processor */
+  SilcFSMThreadStruct event_thread;      /* FSM thread for events */
+  SilcFSMSemaStruct wait_event;                 /* Event signaller */
+  SilcMutex lock;                       /* Connection lock */
+  SilcSchedule schedule;                /* Connection's scheduler */
+  SilcSKE ske;                          /* Key exchange protocol */
+  SilcSKERekeyMaterial rekey;           /* Rekey material */
+  SilcHash hash;                        /* Negotiated hash function */
+  SilcClientConnectionParams params;    /* Connection parameters */
+  SilcAtomic16 cmd_ident;               /* Current command identifier */
+  SilcIDCacheEntry local_entry;                 /* Local client cache entry */
+
+  SilcHashTable privmsg_wait;           /* Waited private messages */
+
+  /* Events */
+  unsigned int connect            : 1;  /* Connect remote host */
+  unsigned int disconnected       : 1;  /* Disconnected by remote host */
+  unsigned int key_exchange       : 1;   /* Start key exchange */
+  unsigned int new_packet         : 1;  /* New packet received */
+
+  /* Flags */
+  unsigned int verbose            : 1;   /* Notify application */
+  unsigned int registering        : 1;  /* Set when registering to network */
 };
 
+SILC_FSM_STATE(silc_client_connection_st_packet);
+
+void silc_client_channel_message(SilcClient client,
+                                SilcClientConnection conn,
+                                SilcPacket packet);
+void silc_client_ftp(SilcClient client, SilcClientConnection conn,
+                    SilcPacket packet);
+void silc_client_channel_key(SilcClient client,
+                            SilcClientConnection conn,
+                            SilcPacket packet);
+void silc_client_notify(SilcClient client,
+                       SilcClientConnection conn,
+                       SilcPacket packet);
+void silc_client_disconnect(SilcClient client,
+                           SilcClientConnection conn,
+                           SilcPacket packet);
+void silc_client_error(SilcClient client,
+                      SilcClientConnection conn,
+                      SilcPacket packet);
+void silc_client_key_agreement(SilcClient client,
+                              SilcClientConnection conn,
+                              SilcPacket packet);
+void silc_client_connection_auth_request(SilcClient client,
+                                        SilcClientConnection conn,
+                                        SilcPacket packet);
+SilcUInt16 silc_client_command_send_argv(SilcClient client,
+                                        SilcClientConnection conn,
+                                        SilcCommand command,
+                                        SilcClientCommandReply reply,
+                                        void *reply_context,
+                                        SilcUInt32 argc,
+                                        unsigned char **argv,
+                                        SilcUInt32 *argv_lens,
+                                        SilcUInt32 *argv_types);
+
+#if 0
 /* Session resuming callback */
 typedef void (*SilcClientResumeSessionCallback)(SilcClient client,
                                                SilcClientConnection conn,
@@ -213,7 +305,7 @@ do {                                                                \
 
 SILC_TASK_CALLBACK_GLOBAL(silc_client_packet_process);
 void silc_client_packet_send(SilcClient client,
-                             SilcSocketConnection sock,
+                             SilcClientConnection conn,
                              SilcPacketType type,
                              void *dst_id,
                              SilcIdType dst_id_type,
@@ -223,7 +315,7 @@ void silc_client_packet_send(SilcClient client,
                              SilcUInt32 data_len,
                              SilcBool force_send);
 int silc_client_packet_send_real(SilcClient client,
-                                SilcSocketConnection sock,
+                                SilcClientConnection conn,
                                 SilcBool force_send);
 void silc_client_ftp_free_sessions(SilcClient client,
                                   SilcClientConnection conn);
@@ -231,27 +323,13 @@ void silc_client_ftp_session_free(SilcClientFtpSession session);
 void silc_client_ftp_session_free_client(SilcClientConnection conn,
                                         SilcClientEntry client_entry);
 void silc_client_close_connection_real(SilcClient client,
-                                      SilcSocketConnection sock,
+                                      SilcClientConnection conn,
                                       SilcClientConnection conn);
-void silc_client_disconnected_by_server(SilcClient client,
-                                       SilcSocketConnection sock,
-                                       SilcBuffer packet);
-void silc_client_error_by_server(SilcClient client,
-                                SilcSocketConnection sock,
-                                SilcBuffer message);
-void silc_client_receive_new_id(SilcClient client,
-                               SilcSocketConnection sock,
-                               SilcIDPayload idp);
+
 void silc_client_save_channel_key(SilcClient client,
                                  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);
@@ -260,26 +338,8 @@ void silc_client_replace_from_channels(SilcClient client,
                                       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_private_message_key(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);
+                                SilcClientConnection conn,
+                                SilcPacket packet);
 SilcBuffer silc_client_get_detach_data(SilcClient client,
                                       SilcClientConnection conn);
 SilcBool silc_client_process_detach_data(SilcClient client,
@@ -291,14 +351,15 @@ void silc_client_resume_session(SilcClient client,
                                SilcClientResumeSessionCallback callback,
                                void *context);
 SilcBuffer silc_client_attributes_process(SilcClient client,
-                                         SilcSocketConnection sock,
+                                         SilcClientConnection conn,
                                          SilcDList attrs);
 void silc_client_packet_queue_purge(SilcClient client,
-                                   SilcSocketConnection sock);
+                                   SilcClientConnection conn);
 SILC_TASK_CALLBACK_GLOBAL(silc_client_rekey_callback);
 void
 silc_client_command_reply_whois_save(SilcClientCommandReplyContext cmd,
                                     SilcStatus status,
                                     SilcBool notify);
+#endif /* 0 */
 
 #endif
index c9b6ab632376fb2d175d55a014162bd25942e9a5..004d4331c546d3419ae7fc11c2d4f38e6b43ea5e 100644 (file)
@@ -4,7 +4,7 @@
 
   Author: Pekka Riikonen <priikone@silcnet.org>
 
-  Copyright (C) 1997 - 2005 Pekka Riikonen
+  Copyright (C) 1997 - 2006 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
@@ -17,9 +17,6 @@
 
 */
 /* $Id$ */
-/* This file includes the Notify packet handling. Notify packets are
-   important packets sent by the server. They tell different things to the
-   client such as nick changes, mode changes etc. */
 
 #include "silc.h"
 #include "silcclient.h"
index 3b43a1f808de058873225f0f786b62d88165696b..6c6c56ca22709c1ed5cf710779b075f8c9062f99 100644 (file)
@@ -4,7 +4,7 @@
 
   Author: Pekka Riikonen <priikone@silcnet.org>
 
-  Copyright (C) 1997 - 2004 Pekka Riikonen
+  Copyright (C) 1997 - 2006 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
 
 */
 /* $Id$ */
-/* This file includes the private message sending and receiving routines
-   and private message key handling routines. */
 
 #include "silc.h"
 #include "silcclient.h"
 #include "client_internal.h"
 
-/* Sends private message to remote client. If private message key has
-   not been set with this client then the message will be encrypted using
-   normal session keys. Private messages are special packets in SILC
-   network hence we need this own function for them. This is similiar
-   to silc_client_packet_send_to_channel except that we send private
-   message. The `data' is the private message. If the `force_send' is
-   TRUE the packet is sent immediately. */
+/************************** Private Message Send ****************************/
+
+/* Sends private message to remote client. */
 
 SilcBool silc_client_send_private_message(SilcClient client,
-                                     SilcClientConnection conn,
-                                     SilcClientEntry client_entry,
-                                     SilcMessageFlags flags,
-                                     unsigned char *data,
-                                     SilcUInt32 data_len,
-                                     SilcBool force_send)
+                                         SilcClientConnection conn,
+                                         SilcClientEntry client_entry,
+                                         SilcMessageFlags flags,
+                                         unsigned char *data,
+                                         SilcUInt32 data_len)
 {
-  SilcSocketConnection sock;
   SilcBuffer buffer;
-  SilcPacketContext packetdata;
-  const SilcBufferStruct packet;
-  SilcCipher cipher;
-  SilcHmac hmac;
-  int block_len;
-  SilcBool ret = FALSE;
-
-  assert(client && conn && client_entry);
-  sock = conn->sock;
+  SilcBool ret;
+
   SILC_LOG_DEBUG(("Sending private message"));
 
+  if (!client || !conn || !client_entry)
+    return FALSE;
+
   /* Encode private message payload */
-  buffer = silc_message_payload_encode(flags, data, data_len,
-                                      !client_entry->send_key ? FALSE :
-                                      !client_entry->generated,
-                                      TRUE, client_entry->send_key,
-                                      client_entry->hmac_send,
-                                      client->rng, NULL, client->private_key,
-                                      client->sha1hash);
+  buffer =
+    silc_message_payload_encode(flags, data, data_len,
+                               (!client_entry->internal.send_key ? FALSE :
+                                !client_entry->internal.generated),
+                               TRUE, client_entry->internal.send_key,
+                               client_entry->internal.hmac_send,
+                               client->rng, NULL, conn->private_key,
+                               client->sha1hash, NULL);
   if (!buffer) {
     SILC_LOG_ERROR(("Error encoding private message"));
     return FALSE;
   }
 
-  /* If we don't have private message specific key then private messages
-     are just as any normal packet thus call normal packet sending.  If
-     the key exist then the encryption process is a bit different and
-     will be done in the rest of this function. */
-  if (!client_entry->send_key) {
-    silc_client_packet_send(client, sock, SILC_PACKET_PRIVATE_MESSAGE,
-                           client_entry->id, SILC_ID_CLIENT, NULL, NULL,
-                           buffer->data, buffer->len, force_send);
-    ret = TRUE;
-    goto out;
-  }
-
-  /* We have private message specific key */
-
-  /* Get data used in the encryption */
-  cipher = conn->internal->send_key;
-  hmac = conn->internal->hmac_send;
-  block_len = silc_cipher_get_block_len(cipher);
-
-  /* Set the packet context pointers. */
-  data = buffer->data;
-  data_len = buffer->len;
-  packetdata.flags = SILC_PACKET_FLAG_PRIVMSG_KEY;
-  packetdata.type = SILC_PACKET_PRIVATE_MESSAGE;
-  packetdata.src_id = conn->local_id_data;
-  packetdata.src_id_len = silc_id_get_len(conn->local_id, SILC_ID_CLIENT);
-  packetdata.src_id_type = SILC_ID_CLIENT;
-  packetdata.dst_id = silc_id_id2str(client_entry->id, SILC_ID_CLIENT);
-  packetdata.dst_id_len = silc_id_get_len(client_entry->id, SILC_ID_CLIENT);
-  packetdata.dst_id_type = SILC_ID_CLIENT;
-  data_len = SILC_PACKET_DATALEN(data_len, SILC_PACKET_HEADER_LEN +
-                                packetdata.src_id_len +
-                                packetdata.dst_id_len);
-  packetdata.truelen = data_len + SILC_PACKET_HEADER_LEN +
-    packetdata.src_id_len + packetdata.dst_id_len;
-  SILC_PACKET_PADLEN((SILC_PACKET_HEADER_LEN +
-                     packetdata.src_id_len +
-                     packetdata.dst_id_len), block_len, packetdata.padlen);
-
-  /* Create the outgoing packet */
-  if (!silc_packet_assemble(&packetdata, client->rng, cipher, hmac, sock,
-                            data, data_len, (const SilcBuffer)&packet)) {
-    SILC_LOG_ERROR(("Error assembling packet"));
-    goto out;
-  }
+  /* Send the private message packet */
+  ret = silc_packet_send_ext(conn->stream, SILC_PACKET_PRIVATE_MESSAGE,
+                            client_entry->internal.send_key ?
+                            SILC_PACKET_FLAG_PRIVMSG_KEY : 0,
+                            0, NULL, SILC_ID_CLIENT, &client_entry->id,
+                            silc_buffer_datalen(buffer), NULL, NULL);
 
-  /* Encrypt the header and padding of the packet. */
-  silc_packet_encrypt(cipher, hmac, conn->internal->psn_send++,
-                     (SilcBuffer)&packet, SILC_PACKET_HEADER_LEN +
-                     packetdata.src_id_len + packetdata.dst_id_len +
-                     packetdata.padlen);
-
-  SILC_LOG_HEXDUMP(("Private message packet, len %d", packet.len),
-                  packet.data, packet.len);
-
-  /* Now actually send the packet */
-  silc_client_packet_send_real(client, sock, force_send);
-
-  /* Check for mandatory rekey */
-  if (conn->internal->psn_send == SILC_CLIENT_REKEY_THRESHOLD)
-    silc_schedule_task_add(client->schedule, sock->sock,
-                          silc_client_rekey_callback, sock, 0, 1,
-                          SILC_TASK_TIMEOUT, SILC_TASK_PRI_NORMAL);
-
-  silc_free(packetdata.dst_id);
-
-  ret = TRUE;
-
- out:
   silc_buffer_free(buffer);
-
   return ret;
 }
 
-static void silc_client_private_message_cb(SilcClient client,
-                                          SilcClientConnection conn,
-                                          SilcClientEntry *clients,
-                                          SilcUInt32 clients_count,
-                                          void *context)
+/************************* Private Message Receive **************************/
+
+/* Private message waiting context */
+typedef struct {
+  SilcMutex wait_lock;
+  SilcCond wait_cond;
+  SilcDList message_queue;
+  unsigned int stopped      : 1;
+} *SilcClientPrivateMessageWait;
+
+/* Client resolving callback.  This continues the private message packet
+   processing in the packet processor thread, which is in waiting state
+   (for incoming packets) when we get here.  We can safely continue in
+   the thread and then return back to waiting when we do it synchronously. */
+
+static void silc_client_private_message_resolved(SilcClient client,
+                                                SilcClientConnection conn,
+                                                SilcStatus status,
+                                                SilcDList clients,
+                                                void *context)
 {
-  SilcPacketContext *packet = (SilcPacketContext *)context;
-
   if (!clients) {
-    silc_packet_context_free(packet);
+    silc_packet_free(context);
     return;
   }
 
-  silc_client_private_message(client, conn->sock, packet);
-  silc_packet_context_free(packet);
+  /* Continue processing the private message packet */
+  silc_fsm_set_state_context(&conn->internal->packet_thread, context);
+  silc_fsm_next(&conn->internal->packet_thread, silc_client_private_message);
+  silc_fsm_continue_sync(&conn->internal->packet_thread);
 }
 
-/* Private message received. This processes the private message and
-   finally displays it on the screen. */
+/* Private message received. */
 
-void silc_client_private_message(SilcClient client,
-                                SilcSocketConnection sock,
-                                SilcPacketContext *packet)
+SILC_FSM_STATE(silc_client_private_message)
 {
-  SilcClientConnection conn = (SilcClientConnection)sock->user_data;
+  SilcClientConnection conn = fsm_context;
+  SilcClient client = conn->client;
+  SilcPacket packet = state_context;
   SilcMessagePayload payload = NULL;
-  SilcClientID *remote_id = NULL;
+  SilcClientID remote_id;
   SilcClientEntry remote_client;
   SilcMessageFlags flags;
   unsigned char *message;
   SilcUInt32 message_len;
-  SilcCipher cipher = NULL;
-  SilcHmac hmac = NULL;
+  SilcClientPrivateMessageWait pmw;
 
   if (packet->src_id_type != SILC_ID_CLIENT)
     goto out;
 
-  remote_id = silc_id_str2id(packet->src_id, packet->src_id_len,
-                            SILC_ID_CLIENT);
-  if (!remote_id)
+  if (!silc_id_str2id(packet->src_id, packet->src_id_len, SILC_ID_CLIENT,
+                     &remote_id, sizeof(remote_id)))
     goto out;
 
   /* Check whether we know this client already */
-  remote_client = silc_client_get_client_by_id(client, conn, remote_id);
+  remote_client = silc_client_get_client_by_id(client, conn, &remote_id);
   if (!remote_client || !remote_client->nickname) {
-    if (remote_client) {
-      if (remote_client->status & SILC_CLIENT_STATUS_RESOLVING) {
-       remote_client->status &= ~SILC_CLIENT_STATUS_RESOLVING;
-       goto out;
-      }
-      remote_client->status |= SILC_CLIENT_STATUS_RESOLVING;
-      remote_client->resolve_cmd_ident = conn->cmd_ident + 1;
-    }
-
-    /* Resolve the client info */
-    silc_client_get_client_by_id_resolve(client, conn, remote_id, NULL,
-                                        silc_client_private_message_cb,
-                                        silc_packet_context_dup(packet));
-    return;
+    /* Resolve the client info.  We return back to packet thread to receive
+       other packets while we wait for the resolving to finish. */
+    silc_client_get_client_by_id_resolve(client, conn, &remote_id, NULL,
+                                        silc_client_private_message_resolved,
+                                        packet);
+    silc_fsm_next(fsm, silc_client_connection_st_packet);
+    return SILC_FSM_CONTINUE;
   }
 
-  cipher = remote_client->receive_key;
-  hmac = remote_client->hmac_receive;
-  if (packet->flags & SILC_PACKET_FLAG_PRIVMSG_KEY && !cipher && !hmac) {
-    silc_free(remote_id);
-    return;
-  }
+  if (packet->flags & SILC_PACKET_FLAG_PRIVMSG_KEY &&
+      !remote_client->internal.receive_key &&
+      !remote_client->internal.hmac_receive)
+    goto out;
 
   /* Parse the payload and decrypt it also if private message key is set */
-  payload = silc_message_payload_parse(packet->buffer->data,
-                                      packet->buffer->len, TRUE,
-                                      !remote_client->generated,
-                                      cipher, hmac);
-  if (!payload) {
-    silc_free(remote_id);
-    return;
-  }
+  payload =
+    silc_message_payload_parse(silc_buffer_datalen(&packet->buffer),
+                              TRUE, !remote_client->internal.generated,
+                              remote_client->internal.receive_key,
+                              remote_client->internal.hmac_receive,
+                              NULL, FALSE, NULL);
+  if (!payload)
+    goto out;
 
-  flags = silc_message_get_flags(payload);
+#if 0 /* We need to rethink this.  This doesn't work with multiple
+        waiters, and performance is suboptimal. */
+  /* Check if some thread is waiting for this private message */
+  silc_mutex_lock(conn->internal->lock);
+  if (conn->internal->privmsg_wait &&
+      silc_hash_table_find_ext(conn->internal->privmsg_wait,
+                              &remote_client->id, NULL, (void **)&pmw,
+                              NULL, NULL, silc_hash_id_compare_full,
+                              SILC_32_TO_PTR(SILC_ID_CLIENT))) {
+    /* Signal that message was received */
+    silc_mutex_unlock(conn->internal->lock);
+    silc_mutex_lock(pmw->wait_lock);
+    if (!pmw->stopped) {
+      silc_dlist_add(pmw->message_queue, payload);
+      silc_cond_broadcast(pmw->wait_cond);
+      silc_mutex_unlock(pmw->wait_lock);
+      silc_packet_free(packet);
+      goto out;
+    }
+    silc_mutex_unlock(pmw->wait_lock);
+  } else
+    silc_mutex_unlock(conn->internal->lock);
+#endif /* 0 */
 
   /* Pass the private message to application */
+  flags = silc_message_get_flags(payload);
   message = silc_message_get_data(payload, &message_len);
   client->internal->ops->private_message(client, conn, remote_client, payload,
                                         flags, message, message_len);
@@ -230,7 +182,7 @@ void silc_client_private_message(SilcClient client,
   if (conn->internal->away && conn->internal->away->away &&
       !(flags & SILC_MESSAGE_FLAG_NOREPLY)) {
     /* If it's me, ignore */
-    if (SILC_ID_CLIENT_COMPARE(remote_id, conn->local_id))
+    if (SILC_ID_CLIENT_COMPARE(&remote_id, conn->local_id))
       goto out;
 
     /* Send the away message */
@@ -238,75 +190,217 @@ void silc_client_private_message(SilcClient client,
                                     SILC_MESSAGE_FLAG_AUTOREPLY |
                                     SILC_MESSAGE_FLAG_NOREPLY,
                                     conn->internal->away->away,
-                                    strlen(conn->internal->away->away), TRUE);
+                                    strlen(conn->internal->away->away));
   }
 
  out:
+  /** Packet processed */
   if (payload)
     silc_message_payload_free(payload);
-  silc_free(remote_id);
+  silc_packet_free(packet);
+  silc_fsm_next(fsm, silc_client_connection_st_packet);
+  return SILC_FSM_CONTINUE;
+}
+
+#if 0 /* XXX we need to rethink this */
+/* Initialize private message waiting in a thread. */
+
+void *silc_client_private_message_wait_init(SilcClientConnection conn,
+                                           SilcClientEntry client_entry)
+{
+  SilcClientPrivateMessageWait pmw;
+
+  pmw = silc_calloc(1, sizeof(*pmw));
+  if (!pmw)
+    return NULL;
+
+  pmw->message_queue = silc_dlist_init();
+  if (!pmw->message_queue) {
+    silc_free(pmw);
+    return NULL;
+  }
+
+  /* Allocate mutex and conditional variable */
+  if (!silc_mutex_alloc(&pmw->wait_lock)) {
+    silc_dlist_uninit(pmw->message_queue);
+    silc_free(pmw);
+    return NULL;
+  }
+  if (!silc_cond_alloc(&pmw->wait_cond)) {
+    silc_dlist_uninit(pmw->message_queue);
+    silc_mutex_free(pmw->wait_lock);
+    silc_free(pmw);
+    return NULL;
+  }
+
+  silc_mutex_lock(conn->internal->lock);
+
+  /* Allocate waiting hash table */
+  if (!conn->internal->privmsg_wait) {
+    conn->internal->privmsg_wait =
+      silc_hash_table_alloc(0, silc_hash_id,
+                           SILC_32_TO_PTR(SILC_ID_CLIENT),
+                           silc_hash_id_compare,
+                           SILC_32_TO_PTR(SILC_ID_CLIENT), NULL, NULL, TRUE);
+    if (!conn->internal->privmsg_wait) {
+      silc_mutex_unlock(conn->internal->lock);
+      silc_dlist_uninit(pmw->message_queue);
+      silc_mutex_free(pmw->wait_lock);
+      silc_cond_free(pmw->wait_cond);
+      silc_free(pmw);
+      return NULL;
+    }
+  }
+
+  /* Add to waiting hash table */
+  silc_hash_table_add(conn->internal->privmsg_wait, client_entry->id, pmw);
+
+  silc_mutex_unlock(conn->internal->lock);
+
+  return (void *)pmw;
+}
+
+/* Uninitialize private message waiting. */
+
+void silc_client_private_message_wait_uninit(SilcClientConnection conn,
+                                            SilcClientEntry client_entry,
+                                            void *waiter)
+{
+  SilcClientPrivateMessageWait pmw = waiter;
+  SilcMessagePayload payload;
+
+  /* Signal any threads to stop waiting */
+  silc_mutex_lock(pmw->wait_lock);
+  pmw->stopped = TRUE;
+  silc_cond_broadcast(pmw->wait_cond);
+  silc_mutex_unlock(pmw->wait_lock);
+
+  /* Re-acquire lock and free resources */
+  silc_mutex_lock(pmw->wait_lock);
+
+  /* Free any remaining message */
+  silc_dlist_start(pmw->message_queue);
+  while ((payload = silc_dlist_get(pmw->message_queue)))
+    silc_message_payload_free(payload);
+
+  silc_dlist_uninit(pmw->message_queue);
+  silc_cond_free(pmw->wait_cond);
+  silc_mutex_unlock(pmw->wait_lock);
+  silc_mutex_free(pmw->wait_lock);
+
+  silc_mutex_lock(conn->internal->lock);
+  silc_hash_table_del_by_context(conn->internal->privmsg_wait,
+                                client_entry->id, pmw);
+  silc_mutex_unlock(conn->internal->lock);
+
+  silc_free(pmw);
+}
+
+/* Blocks the calling process or thread until a private message has been
+   received from the specified client. */
+
+SilcBool silc_client_private_message_wait(SilcClientConnection conn,
+                                         SilcClientEntry client_entry,
+                                         void *waiter,
+                                         SilcMessagePayload *payload)
+{
+  SilcClientPrivateMessageWait pmw = waiter;
+  SilcPacket packet;
+
+  silc_mutex_lock(pmw->wait_lock);
+
+  /* Wait here until private message has been received */
+  while (silc_dlist_count(pmw->message_queue) == 0) {
+    if (pmw->stopped) {
+      silc_mutex_unlock(pmw->wait_lock);
+      return FALSE;
+    }
+    silc_cond_wait(pmw->wait_cond, pmw->wait_lock);
+  }
+
+  /* Return message */
+  silc_dlist_start(pmw->message_queue);
+  *payload = silc_dlist_get(pmw->message_queue);
+  silc_dlist_del(pmw->message_queue, *payload);
+
+  silc_mutex_unlock(pmw->wait_lock);
+
+  return TRUE;
 }
+#endif /* 0 */
 
-/* Function that actually employes the received private message key */
+/*************************** Private Message Key ****************************/
+
+/* Client resolving callback.  Here we simply mark that we are the responder
+   side of this private message key request.  */
 
 static void silc_client_private_message_key_cb(SilcClient client,
                                               SilcClientConnection conn,
-                                              SilcClientEntry *clients,
-                                              SilcUInt32 clients_count,
+                                              SilcStatus status,
+                                              SilcDList clients,
                                               void *context)
 {
-  SilcPacketContext *packet = (SilcPacketContext *)context;
-  unsigned char *key;
-  SilcUInt16 key_len;
+  SilcPacket packet = context;
   unsigned char *cipher = NULL, *hmac = NULL;
+  SilcClientEntry client_entry;
   int ret;
 
-  if (!clients)
-    goto out;
+  if (!clients) {
+    silc_packet_free(packet);
+    return;
+  }
 
   /* Parse the private message key payload */
-  ret = silc_buffer_unformat(packet->buffer,
-                            SILC_STR_UI16_NSTRING(&key, &key_len),
+  ret = silc_buffer_unformat(&packet->buffer,
                             SILC_STR_UI16_STRING_ALLOC(&cipher),
                             SILC_STR_UI16_STRING_ALLOC(&hmac),
                             SILC_STR_END);
   if (!ret)
     goto out;
 
-  if (key_len > packet->buffer->len)
-    goto out;
-
   /* Mark that we are responder */
-  clients[0]->prv_resp = TRUE;
+  client_entry = silc_dlist_get(clients);
+  client_entry->internal.prv_resp = TRUE;
+
+  /* XXX we should notify application that remote wants to set up the
+     static key */
 
  out:
   silc_free(cipher);
   silc_free(hmac);
-  silc_packet_context_free(packet);
+  silc_packet_free(packet);
 }
 
 /* Processes incoming Private Message Key payload to indicate that the
    sender whishes to set up a static private message key. */
 
-void silc_client_private_message_key(SilcClient client,
-                                    SilcSocketConnection sock,
-                                    SilcPacketContext *packet)
+SILC_FSM_STATE(silc_client_private_message_key)
 {
-  SilcClientID *remote_id;
+  SilcClientConnection conn = fsm_context;
+  SilcClient client = conn->client;
+  SilcPacket packet = state_context;
+  SilcClientID remote_id;
 
-  if (packet->src_id_type != SILC_ID_CLIENT)
-    return;
+  if (packet->src_id_type != SILC_ID_CLIENT) {
+    silc_packet_free(packet);
+    goto out;
+  }
 
-  remote_id = silc_id_str2id(packet->src_id, packet->src_id_len,
-                            SILC_ID_CLIENT);
-  if (!remote_id)
-    return;
+  if (!silc_id_str2id(packet->src_id, packet->src_id_len, SILC_ID_CLIENT,
+                     &remote_id, sizeof(remote_id))) {
+    silc_packet_free(packet);
+    goto out;
+  }
 
-  silc_client_get_client_by_id_resolve(client, sock->user_data, remote_id,
-                                      NULL,
+  /* Always resolve the remote client.  The actual packet is processed
+     in the resolving callback. */
+  silc_client_get_client_by_id_resolve(client, conn, &remote_id, NULL,
                                       silc_client_private_message_key_cb,
-                                      silc_packet_context_dup(packet));
-  silc_free(remote_id);
+                                      packet);
+
+ out:
+  silc_fsm_next(fsm, silc_client_connection_st_packet);
+  return SILC_FSM_CONTINUE;
 }
 
 /* Adds private message key to the client library. The key will be used to
@@ -331,24 +425,26 @@ void silc_client_private_message_key(SilcClient client,
    otherwise. */
 
 SilcBool silc_client_add_private_message_key(SilcClient client,
-                                        SilcClientConnection conn,
-                                        SilcClientEntry client_entry,
-                                        const char *cipher,
-                                        const char *hmac,
-                                        unsigned char *key,
-                                        SilcUInt32 key_len,
-                                        SilcBool generate_key,
-                                        SilcBool responder)
+                                            SilcClientConnection conn,
+                                            SilcClientEntry client_entry,
+                                            const char *cipher,
+                                            const char *hmac,
+                                            unsigned char *key,
+                                            SilcUInt32 key_len,
+                                            SilcBool generate_key,
+                                            SilcBool responder)
 {
   unsigned char private_key[32];
   SilcUInt32 len;
+  SilcSKEKeyMaterial keymat;
+  SilcBool ret;
   int i;
-  SilcSKEKeyMaterial *keymat;
 
-  assert(client && client_entry);
+  if (!client || !client_entry)
+    return FALSE;
 
   /* Return FALSE if key already set */
-  if (client_entry->send_key && client_entry->receive_key)
+  if (client_entry->internal.send_key && client_entry->internal.receive_key)
     return FALSE;
 
   if (!cipher)
@@ -369,55 +465,30 @@ SilcBool silc_client_add_private_message_key(SilcClient client,
       private_key[i] = silc_rng_get_byte_fast(client->rng);
     key = private_key;
     key_len = len;
-    client_entry->generated = TRUE;
   }
 
   /* Save the key */
-  client_entry->key = silc_memdup(key, key_len);
-  client_entry->key_len = key_len;
+  client_entry->internal.key = silc_memdup(key, key_len);
+  client_entry->internal.key_len = key_len;
 
   /* 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->sha1hash, keymat)
-      != SILC_SKE_STATUS_OK)
+  keymat = silc_ske_process_key_material_data(key, key_len, 16, 256, 16,
+                                             client->sha1hash);
+  if (!keymat)
     return FALSE;
 
-  /* Allocate the cipher and HMAC */
-  silc_cipher_alloc(cipher, &client_entry->send_key);
-  silc_cipher_alloc(cipher, &client_entry->receive_key);
-  silc_hmac_alloc(hmac, NULL, &client_entry->hmac_send);
-  silc_hmac_alloc(hmac, NULL, &client_entry->hmac_receive);
+  /* Set the key into use */
+  ret = silc_client_add_private_message_key_ske(client, conn, client_entry,
+                                               cipher, hmac, keymat,
+                                               responder);
 
-  /* Set the keys */
-  if (responder == TRUE) {
-    silc_cipher_set_key(client_entry->send_key, keymat->receive_enc_key,
-                       keymat->enc_key_len);
-    silc_cipher_set_iv(client_entry->send_key, keymat->receive_iv);
-    silc_cipher_set_key(client_entry->receive_key, keymat->send_enc_key,
-                       keymat->enc_key_len);
-    silc_cipher_set_iv(client_entry->receive_key, keymat->send_iv);
-    silc_hmac_set_key(client_entry->hmac_send, keymat->receive_hmac_key,
-                     keymat->hmac_key_len);
-    silc_hmac_set_key(client_entry->hmac_receive, keymat->send_hmac_key,
-                     keymat->hmac_key_len);
-  } else {
-    silc_cipher_set_key(client_entry->send_key, keymat->send_enc_key,
-                       keymat->enc_key_len);
-    silc_cipher_set_iv(client_entry->send_key, keymat->send_iv);
-    silc_cipher_set_key(client_entry->receive_key, keymat->receive_enc_key,
-                       keymat->enc_key_len);
-    silc_cipher_set_iv(client_entry->receive_key, keymat->receive_iv);
-    silc_hmac_set_key(client_entry->hmac_send, keymat->send_hmac_key,
-                     keymat->hmac_key_len);
-    silc_hmac_set_key(client_entry->hmac_receive, keymat->receive_hmac_key,
-                     keymat->hmac_key_len);
-  }
+  if (!generate_key)
+    client_entry->internal.generated = FALSE;
 
   /* Free the key material */
   silc_ske_free_key_material(keymat);
 
-  return TRUE;
+  return ret;
 }
 
 /* Same as above but takes the key material from the SKE key material
@@ -427,17 +498,18 @@ SilcBool silc_client_add_private_message_key(SilcClient client,
    the SKE protocol. */
 
 SilcBool silc_client_add_private_message_key_ske(SilcClient client,
-                                            SilcClientConnection conn,
-                                            SilcClientEntry client_entry,
-                                            const char *cipher,
-                                            const char *hmac,
-                                            SilcSKEKeyMaterial *key,
-                                            SilcBool responder)
+                                                SilcClientConnection conn,
+                                                SilcClientEntry client_entry,
+                                                const char *cipher,
+                                                const char *hmac,
+                                                SilcSKEKeyMaterial keymat,
+                                                SilcBool responder)
 {
-  assert(client && client_entry);
+  if (!client || !client_entry)
+    return FALSE;
 
   /* Return FALSE if key already set */
-  if (client_entry->send_key && client_entry->receive_key)
+  if (client_entry->internal.send_key && client_entry->internal.receive_key)
     return FALSE;
 
   if (!cipher)
@@ -451,37 +523,51 @@ SilcBool silc_client_add_private_message_key_ske(SilcClient client,
   if (!silc_hmac_is_supported(hmac))
     return FALSE;
 
-  client_entry->generated = TRUE;
+  client_entry->internal.generated = TRUE;
 
   /* Allocate the cipher and HMAC */
-  silc_cipher_alloc(cipher, &client_entry->send_key);
-  silc_cipher_alloc(cipher, &client_entry->receive_key);
-  silc_hmac_alloc(hmac, NULL, &client_entry->hmac_send);
-  silc_hmac_alloc(hmac, NULL, &client_entry->hmac_receive);
+  if (!silc_cipher_alloc(cipher, &client_entry->internal.send_key))
+    return FALSE;
+  if (!silc_cipher_alloc(cipher, &client_entry->internal.receive_key))
+    return FALSE;
+  if (!silc_hmac_alloc(hmac, NULL, &client_entry->internal.hmac_send))
+    return FALSE;
+  if (!silc_hmac_alloc(hmac, NULL, &client_entry->internal.hmac_receive))
+    return FALSE;
 
   /* Set the keys */
   if (responder == TRUE) {
-    silc_cipher_set_key(client_entry->send_key, key->receive_enc_key,
-                       key->enc_key_len);
-    silc_cipher_set_iv(client_entry->send_key, key->receive_iv);
-    silc_cipher_set_key(client_entry->receive_key, key->send_enc_key,
-                       key->enc_key_len);
-    silc_cipher_set_iv(client_entry->receive_key, key->send_iv);
-    silc_hmac_set_key(client_entry->hmac_send, key->receive_hmac_key,
-                     key->hmac_key_len);
-    silc_hmac_set_key(client_entry->hmac_receive, key->send_hmac_key,
-                     key->hmac_key_len);
+    silc_cipher_set_key(client_entry->internal.send_key,
+                       keymat->receive_enc_key,
+                       keymat->enc_key_len);
+    silc_cipher_set_iv(client_entry->internal.send_key,
+                      keymat->receive_iv);
+    silc_cipher_set_key(client_entry->internal.receive_key,
+                       keymat->send_enc_key,
+                       keymat->enc_key_len);
+    silc_cipher_set_iv(client_entry->internal.receive_key, keymat->send_iv);
+    silc_hmac_set_key(client_entry->internal.hmac_send,
+                     keymat->receive_hmac_key,
+                     keymat->hmac_key_len);
+    silc_hmac_set_key(client_entry->internal.hmac_receive,
+                     keymat->send_hmac_key,
+                     keymat->hmac_key_len);
   } else {
-    silc_cipher_set_key(client_entry->send_key, key->send_enc_key,
-                       key->enc_key_len);
-    silc_cipher_set_iv(client_entry->send_key, key->send_iv);
-    silc_cipher_set_key(client_entry->receive_key, key->receive_enc_key,
-                       key->enc_key_len);
-    silc_cipher_set_iv(client_entry->receive_key, key->receive_iv);
-    silc_hmac_set_key(client_entry->hmac_send, key->send_hmac_key,
-                     key->hmac_key_len);
-    silc_hmac_set_key(client_entry->hmac_receive, key->receive_hmac_key,
-                     key->hmac_key_len);
+    silc_cipher_set_key(client_entry->internal.send_key,
+                       keymat->send_enc_key,
+                       keymat->enc_key_len);
+    silc_cipher_set_iv(client_entry->internal.send_key,
+                      keymat->send_iv);
+    silc_cipher_set_key(client_entry->internal.receive_key,
+                       keymat->receive_enc_key,
+                       keymat->enc_key_len);
+    silc_cipher_set_iv(client_entry->internal.receive_key, keymat->receive_iv);
+    silc_hmac_set_key(client_entry->internal.hmac_send,
+                     keymat->send_hmac_key,
+                     keymat->hmac_key_len);
+    silc_hmac_set_key(client_entry->internal.hmac_receive,
+                     keymat->receive_hmac_key,
+                     keymat->hmac_key_len);
   }
 
   return TRUE;
@@ -491,46 +577,48 @@ SilcBool silc_client_add_private_message_key_ske(SilcClient client,
    going to be the initiator, if and when, the users set up a static
    private message key (not Key Agreement). */
 
-SilcBool silc_client_send_private_message_key_request(SilcClient client,
-                                                 SilcClientConnection conn,
-                                                 SilcClientEntry client_entry)
+SilcBool
+silc_client_send_private_message_key_request(SilcClient client,
+                                            SilcClientConnection conn,
+                                            SilcClientEntry client_entry)
 {
-  SilcSocketConnection sock;
-  SilcBuffer buffer;
+  SilcBufferStruct buffer;
   int cipher_len, hmac_len;
   const char *cipher, *hmac;
+  SilcBool ret;
 
-  assert(client && conn && client_entry);
+  if (!client || !conn || !client_entry)
+    return FALSE;
 
-  sock = conn->sock;
-  if (!client_entry->send_key || !client_entry->key)
+  if (!client_entry->internal.send_key && !client_entry->internal.receive_key)
     return FALSE;
 
   SILC_LOG_DEBUG(("Sending private message key indicator"));
 
-  cipher = silc_cipher_get_name(client_entry->send_key);
+  cipher = silc_cipher_get_name(client_entry->internal.send_key);
   cipher_len = strlen(cipher);
-  hmac = silc_hmac_get_name(client_entry->hmac_send);
+  hmac = silc_hmac_get_name(client_entry->internal.hmac_send);
   hmac_len = strlen(hmac);
 
   /* Create private message key payload */
-  buffer = silc_buffer_alloc_size(4 + cipher_len + hmac_len);
-  silc_buffer_format(buffer,
-                    SILC_STR_UI_SHORT(cipher_len),
-                    SILC_STR_UI_XNSTRING(cipher,
-                                         cipher_len),
-                    SILC_STR_UI_SHORT(hmac_len),
-                    SILC_STR_UI_XNSTRING(hmac,
-                                         hmac_len),
-                    SILC_STR_END);
+  memset(&buffer, 0, sizeof(buffer));
+  if (silc_buffer_format(&buffer,
+                        SILC_STR_UI_SHORT(cipher_len),
+                        SILC_STR_UI_XNSTRING(cipher,
+                                             cipher_len),
+                        SILC_STR_UI_SHORT(hmac_len),
+                        SILC_STR_UI_XNSTRING(hmac,
+                                             hmac_len),
+                        SILC_STR_END) < 0)
+    return FALSE;
 
   /* Send the packet */
-  silc_client_packet_send(client, sock, SILC_PACKET_PRIVATE_MESSAGE_KEY,
-                         client_entry->id, SILC_ID_CLIENT, NULL, NULL,
-                         buffer->data, buffer->len, TRUE);
-  silc_free(buffer);
+  ret = silc_packet_send_ext(conn->stream, SILC_PACKET_PRIVATE_MESSAGE_KEY,
+                            0, 0, NULL, SILC_ID_CLIENT, &client_entry->id,
+                            silc_buffer_datalen(&buffer), NULL, NULL);
+  silc_buffer_purge(&buffer);
 
-  return TRUE;
+  return ret;
 }
 
 /* Removes the private message from the library. The key won't be used
@@ -538,25 +626,26 @@ SilcBool silc_client_send_private_message_key_request(SilcClient client,
    client. Returns FALSE on error, TRUE otherwise. */
 
 SilcBool silc_client_del_private_message_key(SilcClient client,
-                                       SilcClientConnection conn,
-                                       SilcClientEntry client_entry)
+                                            SilcClientConnection conn,
+                                            SilcClientEntry client_entry)
 {
-  assert(client && client_entry);
+  if (!client || !client_entry)
+    return FALSE;
 
-  if (!client_entry->send_key && !client_entry->receive_key)
+  if (!client_entry->internal.send_key && !client_entry->internal.receive_key)
     return FALSE;
 
-  silc_cipher_free(client_entry->send_key);
-  silc_cipher_free(client_entry->receive_key);
+  silc_cipher_free(client_entry->internal.send_key);
+  silc_cipher_free(client_entry->internal.receive_key);
 
-  if (client_entry->key) {
-    memset(client_entry->key, 0, client_entry->key_len);
-    silc_free(client_entry->key);
+  if (client_entry->internal.key) {
+    memset(client_entry->internal.key, 0, client_entry->internal.key_len);
+    silc_free(client_entry->internal.key);
   }
 
-  client_entry->send_key = NULL;
-  client_entry->receive_key = NULL;
-  client_entry->key = NULL;
+  client_entry->internal.send_key = NULL;
+  client_entry->internal.receive_key = NULL;
+  client_entry->internal.key = NULL;
 
   return TRUE;
 }
@@ -576,36 +665,33 @@ silc_client_list_private_message_keys(SilcClient client,
 {
   SilcPrivateMessageKeys keys;
   SilcUInt32 count = 0;
+  SilcList list;
   SilcIDCacheEntry id_cache;
-  SilcIDCacheList list;
   SilcClientEntry entry;
 
-  assert(client && conn);
+  if (!client || !conn)
+    return NULL;
 
   if (!silc_idcache_get_all(conn->internal->client_cache, &list))
     return NULL;
 
-  if (!silc_idcache_list_count(list)) {
-    silc_idcache_list_free(list);
+  keys = silc_calloc(silc_list_count(list), sizeof(*keys));
+  if (!keys)
     return NULL;
-  }
-
-  keys = silc_calloc(silc_idcache_list_count(list), sizeof(*keys));
 
-  silc_idcache_list_first(list, &id_cache);
-  while (id_cache) {
-    entry = (SilcClientEntry)id_cache->context;
-
-    if (entry->send_key) {
+  silc_list_start(list);
+  while ((id_cache = silc_list_get(list))) {
+    entry = id_cache->context;
+    if (entry->internal.send_key) {
       keys[count].client_entry = entry;
-      keys[count].cipher = (char *)silc_cipher_get_name(entry->send_key);
-      keys[count].key = entry->generated == FALSE ? entry->key : NULL;
-      keys[count].key_len = entry->generated == FALSE ? entry->key_len : 0;
+      keys[count].cipher = (char *)silc_cipher_get_name(entry->internal.
+                                                       send_key);
+      keys[count].key = (entry->internal.generated == FALSE ?
+                        entry->internal.key : NULL);
+      keys[count].key_len = (entry->internal.generated == FALSE ?
+                            entry->internal.key_len : 0);
       count++;
     }
-
-    if (!silc_idcache_list_next(list, &id_cache))
-      break;
   }
 
   if (key_count)
diff --git a/lib/silcclient/client_prvmsg.h b/lib/silcclient/client_prvmsg.h
new file mode 100644 (file)
index 0000000..b7b8b7f
--- /dev/null
@@ -0,0 +1,26 @@
+/*
+
+  client_prvmsg.h
+
+  Author: Pekka Riikonen <priikone@silcnet.org>
+
+  Copyright (C) 2006 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; 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.
+
+*/
+
+#ifndef CLIENT_PRVMSG_H
+#define CLIENT_PRVMSG_H
+
+SILC_FSM_STATE(silc_client_private_message);
+SILC_FSM_STATE(silc_client_private_message_key);
+
+#endif /* CLIENT_PRVMSG_H */
diff --git a/lib/silcclient/client_register.c b/lib/silcclient/client_register.c
new file mode 100644 (file)
index 0000000..395933d
--- /dev/null
@@ -0,0 +1,154 @@
+/*
+
+  client_register.c
+
+  Author: Pekka Riikonen <priikone@silcnet.org>
+
+  Copyright (C) 2006 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; 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.
+
+*/
+
+#include "silc.h"
+#include "silcclient.h"
+#include "client_internal.h"
+
+/************************** Types and definitions ***************************/
+
+
+/************************ Static utility functions **************************/
+
+
+/************************ Register to SILC network **************************/
+
+/* Register to network */
+
+SILC_FSM_STATE(silc_client_st_register)
+{
+  SilcClientConnection conn = fsm_context;
+  SilcClient client = conn->client;
+  SilcBufferStruct buf;
+  int ret;
+
+  SILC_LOG_DEBUG(("Register to network"));
+
+  memset(&buf, 0, sizeof(buf));
+  ret = silc_buffer_format(&buf,
+                          SILC_STR_UI_SHORT(strlen(client->username)),
+                          SILC_STR_DATA(client->username,
+                                        strlen(client->username)),
+                          SILC_STR_UI_SHORT(strlen(client->realname)),
+                          SILC_STR_DATA(client->realname,
+                                        strlen(client->realname)),
+                          SILC_STR_END);
+  if (ret < 0) {
+    /** Out of memory */
+    silc_fsm_next(fsm, silc_client_st_register_error);
+    return SILC_FSM_CONTINUE;
+  }
+
+  /* Send the packet */
+  if (!silc_packet_send(conn->stream, SILC_PACKET_NEW_CLIENT, 0,
+                       silc_buffer_data(&buf), silc_buffer_len(&buf))) {
+    /** Error sending packet */
+    silc_buffer_purge(&buf);
+    silc_fsm_next(fsm, silc_client_st_register_error);
+    return SILC_FSM_CONTINUE;
+  }
+
+  silc_buffer_purge(&buf);
+
+  /** Wait for new ID */
+  conn->internal->registering = TRUE;
+  silc_fsm_next_later(fsm, silc_client_st_register_complete, 15, 0);
+  return SILC_FSM_WAIT;
+}
+
+/* Wait for NEW_ID packet to arrive */
+
+SILC_FSM_STATE(silc_client_st_register_complete)
+{
+  SilcClientConnection conn = fsm_context;
+  SilcClient client = conn->client;
+
+  if (!conn->local_id) {
+    /* Timeout, ID not received */
+    conn->internal->registering = FALSE;
+    silc_fsm_next(fsm, silc_client_st_register_error);
+    return SILC_FSM_CONTINUE;
+  }
+
+  SILC_LOG_DEBUG(("Registered to network"));
+
+  /* Issue IDENTIFY command for itself to get resolved hostname
+     correctly from server. */
+  silc_client_command_send(client, conn, SILC_COMMAND_IDENTIFY, NULL, NULL,
+                          1, 5, silc_buffer_data(conn->local_idp),
+                          silc_buffer_len(conn->local_idp));
+
+  /* Send NICK command if the nickname was set by the application (and is
+     not same as the username).  Send this with little timeout. */
+  if (client->nickname &&
+      !silc_utf8_strcasecmp(client->nickname, client->username))
+    silc_client_command_send(client, conn, SILC_COMMAND_NICK, NULL, NULL,
+                            1, 1, client->nickname, strlen(client->nickname));
+
+  /* Issue INFO command to fetch the real server name and server
+     information and other stuff. */
+  silc_client_command_send(client, conn, SILC_COMMAND_INFO, NULL, NULL,
+                          1, 2, silc_buffer_data(conn->remote_idp),
+                          silc_buffer_len(conn->remote_idp));
+
+  /* Call connection callback.  We are now inside SILC network. */
+  conn->callback(client, conn, SILC_CLIENT_CONN_SUCCESS, conn->context);
+
+  conn->internal->registering = FALSE;
+  return SILC_FSM_FINISH;
+}
+
+SILC_FSM_STATE(silc_client_st_register_error)
+{
+  SilcClientConnection conn = fsm_context;
+  SilcClient client = conn->client;
+
+  /* XXX */
+  /* Close connection */
+
+  conn->callback(client, conn, SILC_CLIENT_CONN_ERROR, conn->context);
+
+  return SILC_FSM_FINISH;
+}
+
+
+/************************* Resume detached session **************************/
+
+/* Resume detached session */
+
+SILC_FSM_STATE(silc_client_st_resume)
+{
+
+  return SILC_FSM_FINISH;
+}
+
+SILC_FSM_STATE(silc_client_st_resume_new_id)
+{
+  SilcClientConnection conn = fsm_context;
+
+  return SILC_FSM_FINISH;
+}
+
+SILC_FSM_STATE(silc_client_st_resume_error)
+{
+  /* XXX */
+  /* Close connection */
+
+  return SILC_FSM_FINISH;
+}
diff --git a/lib/silcclient/client_register.h b/lib/silcclient/client_register.h
new file mode 100644 (file)
index 0000000..79ef052
--- /dev/null
@@ -0,0 +1,30 @@
+/*
+
+  client_register.h
+
+  Author: Pekka Riikonen <priikone@silcnet.org>
+
+  Copyright (C) 2006 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; 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.
+
+*/
+
+#ifndef CLIENT_REGISTER_H
+#define CLIENT_REGISTER_H
+
+SILC_FSM_STATE(silc_client_st_register);
+SILC_FSM_STATE(silc_client_st_register_complete);
+SILC_FSM_STATE(silc_client_st_register_error);
+SILC_FSM_STATE(silc_client_st_resume);
+SILC_FSM_STATE(silc_client_st_resume_new_id);
+SILC_FSM_STATE(silc_client_st_resume_error);
+
+#endif /* CLIENT_REGISTER_H */
index 935e5fd03c6186c5c2209ac40effb67ecb90c6f9..ce4a03ea546aba268238bb4abc05c13b12b2243c 100644 (file)
@@ -4,7 +4,7 @@
 
   Author: Pekka Riikonen <priikone@silcnet.org>
 
-  Copyright (C) 1997 - 2005 Pekka Riikonen
+  Copyright (C) 1997 - 2006 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
 #include "silcclient.h"
 #include "client_internal.h"
 
-#define SILC_NOT_CONNECTED(x, c) \
-  x->internal->ops->say((x), (c), SILC_CLIENT_MESSAGE_ERROR, \
-          "You are not connected to a server, please connect to server");
+/************************** Types and definitions ***************************/
 
 /* Command operation that is called at the end of all commands.
    Usage: COMMAND(status); */
-#define COMMAND(status) cmd->client->internal->ops->command(cmd->client, \
-  cmd->conn, cmd, TRUE, cmd->command->cmd, (status))
+#define COMMAND(status) cmd->conn->client->internal->ops->command(     \
+  cmd->conn->client, cmd->conn, TRUE, cmd->cmd, (status), cmd->argc, cmd->argv)
 
 /* Error to application. Usage: COMMAND_ERROR(status); */
-#define COMMAND_ERROR(status)                          \
-  cmd->client->internal->ops->command(cmd->client,     \
-  cmd->conn, cmd, FALSE, cmd->command->cmd, (status))
+#define COMMAND_ERROR(status)                                  \
+  cmd->conn->client->internal->ops->command(cmd->conn->client, \
+  cmd->conn, FALSE, cmd->cmd, (status), cmd->argc, cmd->argv)
 
-#define SAY cmd->client->internal->ops->say
+/* Used to register new command */
+#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)
 
-/* Generic function to send any command. The arguments must be sent already
-   encoded into correct form and in correct order. */
+/* Used to unregister command */
+#define SILC_CLIENT_CMDU(func, cmd, name)                              \
+silc_client_command_unregister(client, SILC_COMMAND_##cmd,             \
+                              silc_client_command_##func,              \
+                              silc_client_command_reply_##func)
+
+#define SAY cmd->conn->client->internal->ops->say
+
+/************************ Static utility functions **************************/
+
+/* Return next available command identifier. */
 
-void silc_client_command_send(SilcClient client, SilcClientConnection conn,
-                             SilcCommand command, SilcUInt16 ident,
-                             SilcUInt32 argc, ...)
+static SilcUInt16 silc_client_cmd_ident(SilcClientConnection conn)
 {
-  SilcBuffer packet;
-  va_list ap;
+  SilcUInt16 cmd_ident;
 
-  assert(client && conn);
+  cmd_ident = silc_atomic_add_int16(&conn->internal->cmd_ident, 1);
+  if (!cmd_ident)
+    cmd_ident = silc_atomic_add_int16(&conn->internal->cmd_ident, 1);
 
-  va_start(ap, argc);
+  return cmd_ident;
+}
 
-  packet = silc_command_payload_encode_vap(command, ident, argc, ap);
-  silc_client_packet_send(client, conn->sock, SILC_PACKET_COMMAND,
-                         NULL, 0, NULL, NULL, packet->data,
-                         packet->len, TRUE);
-  silc_buffer_free(packet);
+/* State to finish command thread after an error in resolving command */
+
+SILC_FSM_STATE(silc_client_command_continue_error)
+{
+  /* Destructor will free all resources */
+  return SILC_FSM_FINISH;
+}
+
+/* Command reply callback to continue with the execution of a command.
+   This will continue when first successful reply is received, and ignores
+   the rest.  On the other hand, if only errors are received it will
+   wait for all errors before continuing. */
+
+static SilcBool silc_client_command_continue(SilcClient client,
+                                            SilcClientConnection conn,
+                                            SilcCommand command,
+                                            SilcStatus status,
+                                            SilcStatus error,
+                                            void *context,
+                                            va_list ap)
+{
+  SilcClientCommandContext cmd = context;
+
+  /* Continue immediately when successful reply is received */
+  if (status == SILC_STATUS_OK || !SILC_STATUS_IS_ERROR(error)) {
+    SILC_FSM_CALL_CONTINUE(&cmd->thread);
+    return FALSE;
+  }
+
+  /* Error */
+  COMMAND_ERROR(error);
+
+  /* Continue after last error is received */
+  if (SILC_STATUS_IS_ERROR(status) ||
+      (status == SILC_STATUS_LIST_END && SILC_STATUS_IS_ERROR(error))) {
+    silc_fsm_next(&cmd->thread, silc_client_command_continue_error);
+    SILC_FSM_CALL_CONTINUE(&cmd->thread);
+    return FALSE;
+  }
+
+  return TRUE;
+}
+
+/* Continues after resolving completed. */
+
+static void silc_client_command_resolve_continue(SilcClient client,
+                                                SilcClientConnection conn,
+                                                SilcStatus status,
+                                                SilcDList clients,
+                                                void *context)
+{
+  SilcClientCommandContext cmd = context;
+
+  if (status != SILC_STATUS_OK)
+    silc_fsm_next(&cmd->thread, silc_client_command_continue_error);
+
+  if (clients)
+    silc_dlist_uninit(clients);
+
+  /* Continue with the command */
+  SILC_FSM_CALL_CONTINUE(&cmd->thread);
+}
+
+/* Register command to client */
+
+static SilcBool
+silc_client_command_register(SilcClient client,
+                            SilcCommand command,
+                            const char *name,
+                            SilcFSMStateCallback command_func,
+                            SilcFSMStateCallback command_reply_func,
+                            SilcUInt8 max_args)
+{
+  SilcClientCommand cmd;
+
+  cmd = silc_calloc(1, sizeof(*cmd));
+  cmd->cmd = command;
+  cmd->command = command_func;
+  cmd->reply = command_reply_func;
+  cmd->name = name ? strdup(name) : NULL;
+  cmd->max_args = max_args;
+
+  silc_list_add(client->internal->commands, cmd);
+
+  return TRUE;
+}
+
+/* Unregister command from client */
+
+static SilcBool
+silc_client_command_unregister(SilcClient client,
+                              SilcCommand command,
+                              SilcFSMStateCallback command_func,
+                              SilcFSMStateCallback command_reply_func)
+{
+  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_func &&
+       cmd->reply == command_reply_func) {
+      silc_list_del(client->internal->commands, cmd);
+      silc_free(cmd->name);
+      silc_free(cmd);
+      return TRUE;
+    }
+  }
+
+  return FALSE;
 }
 
 /* Finds and returns a pointer to the command list. Return NULL if the
    command is not found. */
 
-SilcClientCommand silc_client_command_find(SilcClient client,
-                                          const char *name)
+static SilcClientCommand silc_client_command_find(SilcClient client,
+                                                 const char *name)
 {
   SilcClientCommand cmd;
 
-  assert(client);
-
   silc_list_start(client->internal->commands);
   while ((cmd = silc_list_get(client->internal->commands)) != SILC_LIST_END) {
     if (cmd->name && !strcasecmp(cmd->name, name))
@@ -78,21 +191,187 @@ SilcClientCommand silc_client_command_find(SilcClient client,
   return NULL;
 }
 
+/* Free command context and its internals */
+
+static void silc_client_command_free(SilcClientCommandContext cmd)
+{
+  int i;
+
+  for (i = 0; i < cmd->argc; i++)
+    silc_free(cmd->argv[i]);
+  silc_free(cmd->argv);
+  silc_free(cmd->argv_lens);
+  silc_free(cmd->argv_types);
+  silc_free(cmd);
+}
+
+/* Command thread destructor */
+
+static void silc_client_command_destructor(SilcFSMThread thread,
+                                          void *fsm_context,
+                                          void *destructor_context)
+{
+  silc_client_command_free(fsm_context);
+}
+
+/* Add a command pending a command reply.  Used internally by the library. */
+
+static SilcBool
+silc_client_command_add_pending(SilcClientConnection conn,
+                               SilcClientCommandContext cmd,
+                               SilcClientCommandReply reply,
+                               void *context)
+{
+  SilcClientCommandReplyCallback cb;
+
+  silc_mutex_lock(conn->internal->lock);
+
+  /* Add pending callback, if defined */
+  if (reply) {
+    cb = silc_calloc(1, sizeof(*cb));
+    if (!cb) {
+      silc_mutex_unlock(conn->internal->lock);
+      return FALSE;
+    }
+    cb->reply = reply;
+    cb->context = context;
+    silc_list_add(cmd->reply_callbacks, cb);
+  }
+
+  /* Add pending reply */
+  silc_list_add(conn->internal->pending_commands, cmd);
+
+  silc_mutex_unlock(conn->internal->lock);
+
+  return TRUE;
+}
+
+/* Generic function to send any command. The arguments must be sent already
+   encoded into correct format and in correct order.  Arguments come from
+   variable argument list pointer. */
+
+static SilcUInt16 silc_client_command_send_vap(SilcClient client,
+                                              SilcClientConnection conn,
+                                              SilcClientCommandContext cmd,
+                                              SilcCommand command,
+                                              SilcClientCommandReply reply,
+                                              void *reply_context,
+                                              SilcUInt32 argc, va_list ap)
+{
+  SilcBuffer packet;
+
+  SILC_LOG_DEBUG(("Send command %s", silc_get_command_name(command)));
+
+  if (!cmd->cmd_ident)
+    cmd->cmd_ident = silc_client_cmd_ident(conn);
+
+  /* Encode command payload */
+  packet = silc_command_payload_encode_vap(command, cmd->cmd_ident, argc, ap);
+  if (!packet)
+    return 0;
+
+  /* Send the command */
+  if (!silc_packet_send(conn->stream, SILC_PACKET_COMMAND, 0,
+                       silc_buffer_datalen(packet))) {
+    silc_buffer_free(packet);
+    return 0;
+  }
+
+  /* Add the command pending command reply */
+  silc_client_command_add_pending(conn, cmd, reply, reply_context);
+
+  silc_buffer_free(packet);
+
+  return cmd->cmd_ident;
+}
+
+/* Generic function to send any command. The arguments must be sent already
+   encoded into correct format and in correct order.  Arguments come from
+   arrays. */
+
+static SilcUInt16
+silc_client_command_send_arg_array(SilcClient client,
+                                  SilcClientConnection conn,
+                                  SilcClientCommandContext cmd,
+                                  SilcCommand command,
+                                  SilcClientCommandReply reply,
+                                  void *reply_context,
+                                  SilcUInt32 argc,
+                                  unsigned char **argv,
+                                  SilcUInt32 *argv_lens,
+                                  SilcUInt32 *argv_types)
+{
+  SilcBuffer packet;
+
+  SILC_LOG_DEBUG(("Send command %s", silc_get_command_name(command)));
+
+  if (!cmd->cmd_ident)
+    cmd->cmd_ident = silc_client_cmd_ident(conn);
+
+  /* Encode command payload */
+  packet = silc_command_payload_encode(command, argc, argv, argv_lens,
+                                      argv_types, cmd->cmd_ident);
+  if (!packet)
+    return 0;
+
+  /* Send the command */
+  if (!silc_packet_send(conn->stream, SILC_PACKET_COMMAND, 0,
+                       silc_buffer_datalen(packet))) {
+    silc_buffer_free(packet);
+    return 0;
+  }
+
+  /* Add the command pending command reply */
+  silc_client_command_add_pending(conn, cmd, reply, reply_context);
+
+  silc_buffer_free(packet);
+
+  return cmd->cmd_ident;
+}
+
+/* Generic function to send any command. The arguments must be sent already
+   encoded into correct format and in correct order.  This is used internally
+   by the library.  */
+
+static SilcUInt16 silc_client_command_send_va(SilcClientConnection conn,
+                                             SilcClientCommandContext cmd,
+                                             SilcCommand command,
+                                             SilcClientCommandReply reply,
+                                             void *reply_context,
+                                             SilcUInt32 argc, ...)
+{
+  va_list ap;
+  SilcUInt16 cmd_ident;
+
+  va_start(ap, argc);
+  cmd_ident = silc_client_command_send_vap(conn->client, conn, cmd, command,
+                                          reply, reply_context, argc, ap);
+  va_end(ap);
+
+  return cmd_ident;
+}
+
+/****************************** Command API *********************************/
+
 /* Executes a command */
 
-SilcBool silc_client_command_call(SilcClient client,
-                             SilcClientConnection conn,
-                             const char *command_line, ...)
+SilcUInt16 silc_client_command_call(SilcClient client,
+                                   SilcClientConnection conn,
+                                   const char *command_line, ...)
 {
   va_list va;
   SilcUInt32 argc = 0;
   unsigned char **argv = NULL;
   SilcUInt32 *argv_lens = NULL, *argv_types = NULL;
-  SilcClientCommand cmd;
-  SilcClientCommandContext ctx;
+  SilcClientCommand command;
+  SilcClientCommandContext cmd;
   char *arg;
 
-  assert(client);
+  if (!conn) {
+    client->internal->ops->say(client, NULL, SILC_CLIENT_MESSAGE_ERROR,
+      "You are not connected to a server, please connect to server");
+    return 0;
+  }
 
   /* Parse arguments */
   va_start(va, command_line);
@@ -102,206 +381,216 @@ SilcBool silc_client_command_call(SilcClient client,
     /* Get command name */
     command_name = silc_memdup(command_line, strcspn(command_line, " "));
     if (!command_name)
-      return FALSE;
+      return 0;
 
     /* Find command by name */
-    cmd = silc_client_command_find(client, command_name);
-    if (!cmd) {
+    command = silc_client_command_find(client, command_name);
+    if (!command) {
       silc_free(command_name);
-      return FALSE;
+      return 0;
     }
 
     /* Parse command line */
     silc_parse_command_line((char *)command_line, &argv, &argv_lens,
-                           &argv_types, &argc, cmd->max_args);
+                           &argv_types, &argc, command->max_args);
 
     silc_free(command_name);
   } else {
     arg = va_arg(va, char *);
     if (!arg)
-      return FALSE;
+      return 0;
 
     /* Find command by name */
-    cmd = silc_client_command_find(client, arg);
-    if (!cmd)
-      return FALSE;
+    command = silc_client_command_find(client, arg);
+    if (!command)
+      return 0;
 
     while (arg) {
       argv = silc_realloc(argv, sizeof(*argv) * (argc + 1));
       argv_lens = silc_realloc(argv_lens, sizeof(*argv_lens) * (argc + 1));
       argv_types = silc_realloc(argv_types, sizeof(*argv_types) * (argc + 1));
+      if (!argv || !argv_lens || !argv_types)
+       return FALSE;
       argv[argc] = silc_memdup(arg, strlen(arg));
+      if (!argv[argc])
+       return FALSE;
       argv_lens[argc] = strlen(arg);
       argv_types[argc] = argc;
       argc++;
       arg = va_arg(va, char *);
     }
   }
-
-  /* Allocate command context. */
-  ctx = silc_client_command_alloc();
-  ctx->client = client;
-  ctx->conn = conn;
-  ctx->command = cmd;
-  ctx->argc = argc;
-  ctx->argv = argv;
-  ctx->argv_lens = argv_lens;
-  ctx->argv_types = argv_types;
-
-  /* Call the command */
-  cmd->command(ctx, NULL);
-
   va_end(va);
 
-  return TRUE;
+  /* Allocate command context */
+  cmd = silc_calloc(1, sizeof(*cmd));
+  if (!cmd)
+    return 0;
+  cmd->conn = conn;
+  cmd->cmd = command->cmd;
+  cmd->argc = argc;
+  cmd->argv = argv;
+  cmd->argv_lens = argv_lens;
+  cmd->argv_types = argv_types;
+  cmd->cmd_ident = silc_client_cmd_ident(conn);
+  cmd->called = TRUE;
+  cmd->verbose = TRUE;
+
+  /*** Call command */
+  SILC_LOG_DEBUG(("Calling %s command", silc_get_command_name(cmd->cmd)));
+  silc_fsm_thread_init(&cmd->thread, &conn->internal->fsm, cmd,
+                      silc_client_command_destructor, NULL, FALSE);
+  silc_fsm_start_sync(&cmd->thread, command->command);
+
+  return cmd->cmd_ident;
 }
 
-/* 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.  It can be SILC_COMMAND_NONE
-   to match any command with the `ident'.  If `ident' is non-zero
-   the `callback' will be executed when received reply with command
-   identifier `ident'. If there already exists pending command for the
-   specified command, ident, callback and context this function has no
-   effect. */
-
-void silc_client_command_pending(SilcClientConnection conn,
-                                SilcCommand reply_cmd,
-                                SilcUInt16 ident,
-                                SilcCommandCb callback,
-                                void *context)
+/* Generic function to send any command. The arguments must be sent already
+   encoded into correct format and in correct order. */
+
+SilcUInt16 silc_client_command_send(SilcClient client,
+                                   SilcClientConnection conn,
+                                   SilcCommand command,
+                                   SilcClientCommandReply reply,
+                                   void *reply_context,
+                                   SilcUInt32 argc, ...)
 {
-  SilcClientCommandPending *reply;
-
-  assert(conn);
-  reply = silc_calloc(1, sizeof(*reply));
-  reply->reply_cmd = reply_cmd;
-  reply->ident = ident;
-  reply->context = context;
-  reply->callback = callback;
-  silc_dlist_add(conn->internal->pending_commands, reply);
-}
+  SilcClientCommandContext cmd;
+  va_list ap;
 
-/* Deletes pending command by reply command type. */
+  if (!conn || !reply)
+    return 0;
 
-void silc_client_command_pending_del(SilcClientConnection conn,
-                                    SilcCommand reply_cmd,
-                                    SilcUInt16 ident)
-{
-  SilcClientCommandPending *r;
+  /* Allocate command context */
+  cmd = silc_calloc(1, sizeof(*cmd));
+  if (!cmd)
+    return 0;
+  cmd->conn = conn;
+  cmd->cmd = command;
 
-  if (!conn->internal->pending_commands)
-    return;
+  /* Send the command */
+  va_start(ap, argc);
+  cmd->cmd_ident =
+    silc_client_command_send_vap(client, conn, cmd, command, reply,
+                                reply_context, argc, ap);
+  va_end(ap);
 
-  silc_dlist_start(conn->internal->pending_commands);
-  while ((r = silc_dlist_get(conn->internal->pending_commands))
-        != SILC_LIST_END) {
-    if ((r->reply_cmd == reply_cmd || (r->reply_cmd == SILC_COMMAND_NONE &&
-                                      r->reply_check))
-       && r->ident == ident) {
-      silc_dlist_del(conn->internal->pending_commands, r);
-      silc_free(r);
-    }
+  if (!cmd->cmd_ident) {
+    silc_client_command_free(cmd);
+    return 0;
   }
-}
 
-/* Checks for pending commands and marks callbacks to be called from
-   the command reply function. */
+  /*** Wait for command reply */
+  silc_fsm_thread_init(&cmd->thread, &conn->internal->fsm, cmd,
+                      silc_client_command_destructor, NULL, FALSE);
+  silc_fsm_start_sync(&cmd->thread, silc_client_command_reply_wait);
 
-SilcClientCommandPendingCallbacks
-silc_client_command_pending_check(SilcClientConnection conn,
-                                 SilcClientCommandReplyContext ctx,
-                                 SilcCommand command,
-                                 SilcUInt16 ident,
-                                 SilcUInt32 *callbacks_count)
+  return cmd->cmd_ident;
+}
+
+/* Generic function to send any command. The arguments must be sent already
+   encoded into correct format and in correct order.  Arguments come from
+   arrays. */
+
+SilcUInt16 silc_client_command_send_argv(SilcClient client,
+                                        SilcClientConnection conn,
+                                        SilcCommand command,
+                                        SilcClientCommandReply reply,
+                                        void *reply_context,
+                                        SilcUInt32 argc,
+                                        unsigned char **argv,
+                                        SilcUInt32 *argv_lens,
+                                        SilcUInt32 *argv_types)
 {
-  SilcClientCommandPending *r;
-  SilcClientCommandPendingCallbacks callbacks = NULL;
-  int i = 0;
-
-  silc_dlist_start(conn->internal->pending_commands);
-  while ((r = silc_dlist_get(conn->internal->pending_commands))
-        != SILC_LIST_END) {
-    if ((r->reply_cmd == command || r->reply_cmd == SILC_COMMAND_NONE)
-       && r->ident == ident) {
-      callbacks = silc_realloc(callbacks, sizeof(*callbacks) * (i + 1));
-      callbacks[i].context = r->context;
-      callbacks[i].callback = r->callback;
-      r->reply_check = TRUE;
-      ctx->ident = ident;
-      i++;
-    }
+  SilcClientCommandContext cmd;
+
+  if (!conn || !reply)
+    return 0;
+
+  /* Allocate command context */
+  cmd = silc_calloc(1, sizeof(*cmd));
+  if (!cmd)
+    return 0;
+  cmd->conn = conn;
+  cmd->cmd = command;
+
+  /* Send the command */
+  cmd->cmd_ident =
+    silc_client_command_send_arg_array(client, conn, cmd, command, reply,
+                                      reply_context, argc, argv, argv_lens,
+                                      argv_types);
+  if (!cmd->cmd_ident) {
+    silc_client_command_free(cmd);
+    return 0;
   }
 
-  *callbacks_count = i;
-  return callbacks;
+  /*** Wait for command reply */
+  silc_fsm_thread_init(&cmd->thread, &conn->internal->fsm, cmd,
+                      silc_client_command_destructor, NULL, FALSE);
+  silc_fsm_start_sync(&cmd->thread, silc_client_command_reply_wait);
+
+  return cmd->cmd_ident;
 }
 
-/* Allocate Command Context */
+/* Attach to a command and command identifier to receive command reply. */
 
-SilcClientCommandContext silc_client_command_alloc(void)
+SilcBool silc_client_command_pending(SilcClientConnection conn,
+                                    SilcCommand command,
+                                    SilcUInt16 ident,
+                                    SilcClientCommandReply reply,
+                                    void *context)
 {
-  SilcClientCommandContext ctx = silc_calloc(1, sizeof(*ctx));
-  ctx->users++;
-  return ctx;
-}
+  SilcClientCommandContext cmd;
+  SilcClientCommandReplyCallback cb;
 
-/* Free command context and its internals */
+  if (!conn || !reply)
+    return FALSE;
 
-void silc_client_command_free(SilcClientCommandContext ctx)
-{
-  ctx->users--;
-  SILC_LOG_DEBUG(("Command context %p refcnt %d->%d", ctx, ctx->users + 1,
-                 ctx->users));
-  if (ctx->users < 1) {
-    int i;
-
-    for (i = 0; i < ctx->argc; i++)
-      silc_free(ctx->argv[i]);
-    silc_free(ctx->argv);
-    silc_free(ctx->argv_lens);
-    silc_free(ctx->argv_types);
-    silc_free(ctx);
-  }
-}
+  SILC_LOG_DEBUG(("Add pending command reply for ident %d", ident));
 
-/* Duplicate Command Context by adding reference counter. The context won't
-   be free'd untill it hits zero. */
+  silc_mutex_lock(conn->internal->lock);
 
-SilcClientCommandContext silc_client_command_dup(SilcClientCommandContext ctx)
-{
-  ctx->users++;
-  SILC_LOG_DEBUG(("Command context %p refcnt %d->%d", ctx, ctx->users - 1,
-                 ctx->users));
-  return ctx;
+  /* Find the pending command */
+  silc_list_start(conn->internal->pending_commands);
+  while ((cmd = silc_list_get(conn->internal->pending_commands)))
+    if ((cmd->cmd == command || command == SILC_COMMAND_NONE)
+       && cmd->cmd_ident == ident) {
+
+      /* Add the callback */
+      cb = silc_calloc(1, sizeof(*cb));
+      if (!cb)
+       continue;
+      cb->reply = reply;
+      cb->context = context;
+      silc_list_add(cmd->reply_callbacks, cb);
+    }
+
+  silc_mutex_unlock(conn->internal->lock);
+
+  return FALSE;
 }
 
+/******************************** WHOIS *************************************/
+
 /* Command WHOIS. This command is used to query information about
    specific user. */
 
-SILC_CLIENT_CMD_FUNC(whois)
+SILC_FSM_STATE(silc_client_command_whois)
 {
-  SilcClientCommandContext cmd = (SilcClientCommandContext)context;
+  SilcClientCommandContext cmd = fsm_context;
   SilcClientConnection conn = cmd->conn;
-  SilcBuffer buffer, attrs = NULL;
+  SilcClient client = conn->client;
+  SilcBuffer attrs = NULL;
   unsigned char count[4], *tmp = NULL;
-  int i;
   SilcBool details = FALSE, nick = FALSE;
   unsigned char *pubkey = NULL;
-
-  if (!cmd->conn) {
-    SILC_NOT_CONNECTED(cmd->client, cmd->conn);
-    COMMAND_ERROR(SILC_STATUS_ERR_NOT_REGISTERED);
-    goto out;
-  }
+  int i;
 
   /* 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_command_send(cmd->client, cmd->conn, SILC_COMMAND_WHOIS,
-                            ++conn->cmd_ident,
-                            1, 4, buffer->data, buffer->len);
-    silc_buffer_free(buffer);
+    silc_client_command_send(conn->client, conn, SILC_COMMAND_WHOIS,
+                            NULL, NULL, 1,
+                            4, silc_buffer_datalen(conn->local_idp));
     goto out;
   }
 
@@ -325,8 +614,8 @@ SILC_CLIENT_CMD_FUNC(whois)
   }
 
   if (details) {
-    /* if pubkey is set, add all attributes to the
-       attrs buffer, except public key */
+    /* If pubkey is set, add all attributes to the attrs buffer, except
+       public key */
     if (pubkey) {
       attrs = silc_client_attributes_request(SILC_ATTRIBUTE_USER_INFO,
                                              SILC_ATTRIBUTE_SERVICE,
@@ -337,7 +626,8 @@ SILC_CLIENT_CMD_FUNC(whois)
                                              SILC_ATTRIBUTE_PREFERRED_CONTACT,
                                              SILC_ATTRIBUTE_TIMEZONE,
                                              SILC_ATTRIBUTE_GEOLOCATION,
-                                             SILC_ATTRIBUTE_DEVICE_INFO, 0);
+                                             SILC_ATTRIBUTE_DEVICE_INFO,
+                                            SILC_ATTRIBUTE_USER_ICON, 0);
     } else {
       attrs = silc_client_attributes_request(0);
     }
@@ -347,17 +637,31 @@ SILC_CLIENT_CMD_FUNC(whois)
     SilcAttributeObjPk obj;
     SilcPublicKey pk;
 
-    if (!silc_pkcs_load_public_key(pubkey, &pk, SILC_PKCS_FILE_PEM)) {
-      if (!silc_pkcs_load_public_key(pubkey, &pk, SILC_PKCS_FILE_BIN)) {
-       SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_ERROR,
-           "Could not load public key %s, check the filename",
-           pubkey);
-       COMMAND_ERROR(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
-       goto out;
-      }
+    if (!silc_pkcs_load_public_key(pubkey, &pk)) {
+      SAY(client, conn, SILC_CLIENT_MESSAGE_ERROR,
+         "Could not load public key %s, check the filename",
+         pubkey);
+      COMMAND_ERROR(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
+      goto out;
     }
 
-    obj.type = "silc-rsa";
+    switch (silc_pkcs_get_type(pk)) {
+    case SILC_PKCS_SILC:
+      obj.type = "silc-rsa";
+      break;
+    case SILC_PKCS_SSH2:
+      obj.type = "ssh-rsa";
+      break;
+    case SILC_PKCS_X509V3:
+      obj.type = "x509v3-sign-rsa";
+      break;
+    case SILC_PKCS_OPENPGP:
+      obj.type = "pgp-sign-rsa";
+      break;
+    default:
+      goto out;
+      break;
+    }
     obj.data = silc_pkcs_public_key_encode(pk, &obj.data_len);
 
     attrs = silc_attribute_payload_encode(attrs,
@@ -366,157 +670,123 @@ SILC_CLIENT_CMD_FUNC(whois)
                                           &obj, sizeof(obj));
   }
 
-  buffer = silc_command_payload_encode_va(SILC_COMMAND_WHOIS,
-                                          ++conn->cmd_ident, 3,
-                                          1, nick ? cmd->argv[1] : NULL,
-                                          nick ? cmd->argv_lens[1] : 0,
-                                          2, tmp ? tmp : NULL, tmp ? 4 : 0,
-                                          3, attrs ? attrs->data : NULL,
-                                          attrs ? attrs->len : 0);
-
-  silc_client_packet_send(cmd->client, cmd->conn->sock,
-                         SILC_PACKET_COMMAND, NULL, 0, NULL, NULL,
-                         buffer->data, buffer->len, TRUE);
-  silc_buffer_free(buffer);
+  /* Send command */
+  silc_client_command_send_va(conn, cmd, cmd->cmd, NULL, NULL,
+                             3, 1, nick ? cmd->argv[1] : NULL,
+                             nick ? cmd->argv_lens[1] : 0,
+                             2, tmp ? tmp : NULL, tmp ? 4 : 0,
+                             3, silc_buffer_datalen(attrs));
 
   /* Notify application */
   COMMAND(SILC_STATUS_OK);
 
+  /** Wait for command reply */
+  silc_fsm_next(fsm, silc_client_command_reply_wait);
+  return SILC_FSM_CONTINUE;
+
  out:
-  silc_client_command_free(cmd);
+  return SILC_FSM_FINISH;
 }
 
+/******************************** WHOWAS ************************************/
+
 /* Command WHOWAS. This command is used to query history information about
    specific user that used to exist in the network. */
 
-SILC_CLIENT_CMD_FUNC(whowas)
+SILC_FSM_STATE(silc_client_command_whowas)
 {
-  SilcClientCommandContext cmd = (SilcClientCommandContext)context;
+  SilcClientCommandContext cmd = fsm_context;
   SilcClientConnection conn = cmd->conn;
-  SilcBuffer buffer;
   unsigned char count[4];
-
-  if (!cmd->conn) {
-    SILC_NOT_CONNECTED(cmd->client, cmd->conn);
-    COMMAND_ERROR(SILC_STATUS_ERR_NOT_REGISTERED);
-    goto out;
-  }
+  int c;
 
   if (cmd->argc < 2 || cmd->argc > 3) {
-    SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_INFO,
+    SAY(conn->client, conn, SILC_CLIENT_MESSAGE_INFO,
        "Usage: /WHOWAS <nickname>[@<server>] [<count>]");
     COMMAND_ERROR((cmd->argc < 2 ? SILC_STATUS_ERR_NOT_ENOUGH_PARAMS :
                   SILC_STATUS_ERR_TOO_MANY_PARAMS));
-    goto out;
+    return SILC_FSM_FINISH;
   }
 
   if (cmd->argc == 2) {
-    buffer = silc_command_payload_encode_va(SILC_COMMAND_WHOWAS,
-                                           ++conn->cmd_ident, 1,
-                                           1, cmd->argv[1],
-                                           cmd->argv_lens[1]);
+    silc_client_command_send_va(conn, cmd, cmd->cmd, NULL, NULL,
+                               1, 1, cmd->argv[1], cmd->argv_lens[1]);
   } else {
-    int c = atoi(cmd->argv[2]);
-    memset(count, 0, sizeof(count));
+    c = atoi(cmd->argv[2]);
     SILC_PUT32_MSB(c, count);
-    buffer = silc_command_payload_encode_va(SILC_COMMAND_WHOWAS,
-                                           ++conn->cmd_ident, 2,
-                                           1, cmd->argv[1], cmd->argv_lens[1],
-                                           2, count, sizeof(count));
-  }
-  silc_client_packet_send(cmd->client, cmd->conn->sock,
-                         SILC_PACKET_COMMAND, NULL, 0, NULL, NULL,
-                         buffer->data, buffer->len, TRUE);
-  silc_buffer_free(buffer);
+    silc_client_command_send_va(conn, cmd, cmd->cmd, NULL, NULL,
+                               2, 1, cmd->argv[1], cmd->argv_lens[1],
+                               2, count, sizeof(count));
+  }
 
   /* Notify application */
   COMMAND(SILC_STATUS_OK);
 
- out:
-  silc_client_command_free(cmd);
+  /** Wait for command reply */
+  silc_fsm_next(fsm, silc_client_command_reply_wait);
+  return SILC_FSM_CONTINUE;
 }
 
-/* Command IDENTIFY. This command is used to query information about
-   specific user, especially ID's.
+/******************************** IDENTIFY **********************************/
 
-   NOTE: This command is used only internally by the client library
-   and application MUST NOT call this command directly. */
+/* Command IDENTIFY. This command is used to query information about
+   specific user, especially ID's. */
 
-SILC_CLIENT_CMD_FUNC(identify)
+SILC_FSM_STATE(silc_client_command_identify)
 {
-  SilcClientCommandContext cmd = (SilcClientCommandContext)context;
+  SilcClientCommandContext cmd = fsm_context;
   SilcClientConnection conn = cmd->conn;
-  SilcBuffer buffer;
   unsigned char count[4];
-
-  if (!cmd->conn) {
-    SILC_NOT_CONNECTED(cmd->client, cmd->conn);
-    COMMAND_ERROR(SILC_STATUS_ERR_NOT_REGISTERED);
-    goto out;
-  }
+  int c;
 
   if (cmd->argc < 2 || cmd->argc > 3)
-    goto out;
+    return SILC_FSM_FINISH;
 
   if (cmd->argc == 2) {
-    buffer = silc_command_payload_encode_va(SILC_COMMAND_IDENTIFY,
-                                           ++conn->cmd_ident, 1,
-                                           1, cmd->argv[1],
-                                           cmd->argv_lens[1]);
+    silc_client_command_send_va(conn, cmd, cmd->cmd, NULL, NULL,
+                               1, 1, cmd->argv[1], cmd->argv_lens[1]);
   } else {
-    int c = atoi(cmd->argv[2]);
-    memset(count, 0, sizeof(count));
+    c = atoi(cmd->argv[2]);
     SILC_PUT32_MSB(c, count);
-    buffer = silc_command_payload_encode_va(SILC_COMMAND_IDENTIFY,
-                                           ++conn->cmd_ident, 2,
-                                           1, cmd->argv[1],
-                                           cmd->argv_lens[1],
-                                           4, count, sizeof(count));
+    silc_client_command_send_va(conn, cmd, cmd->cmd, NULL, NULL,
+                               2, 1, cmd->argv[1], cmd->argv_lens[1],
+                               4, count, sizeof(count));
   }
 
-  silc_client_packet_send(cmd->client, cmd->conn->sock,
-                         SILC_PACKET_COMMAND, NULL, 0, NULL, NULL,
-                         buffer->data, buffer->len, TRUE);
-  silc_buffer_free(buffer);
-
- out:
-  silc_client_command_free(cmd);
+  /** Wait for command reply */
+  silc_fsm_next(fsm, silc_client_command_reply_wait);
+  return SILC_FSM_CONTINUE;
 }
 
+/********************************** NICK ************************************/
+
 /* Command NICK. Shows current nickname/sets new nickname on current
    window. */
 
-SILC_CLIENT_CMD_FUNC(nick)
+SILC_FSM_STATE(silc_client_command_nick)
 {
-  SilcClientCommandContext cmd = (SilcClientCommandContext)context;
+  SilcClientCommandContext cmd = fsm_context;
   SilcClientConnection conn = cmd->conn;
-  SilcBuffer buffer;
-
-  if (!cmd->conn) {
-    SILC_NOT_CONNECTED(cmd->client, cmd->conn);
-    COMMAND_ERROR(SILC_STATUS_ERR_NOT_REGISTERED);
-    goto out;
-  }
 
   if (cmd->argc < 2) {
-    SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_INFO,
+    SAY(conn->client, conn, SILC_CLIENT_MESSAGE_INFO,
        "Usage: /NICK <nickname>");
     COMMAND_ERROR(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
     goto out;
   }
 
-  if (silc_utf8_strcasecmp(conn->nickname, cmd->argv[1]))
+  if (silc_utf8_strcasecmp(conn->local_entry->nickname, cmd->argv[1]))
     goto out;
 
   /* Show current nickname */
   if (cmd->argc < 2) {
     if (cmd->conn) {
-      SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_INFO,
+      SAY(conn->client, conn, SILC_CLIENT_MESSAGE_INFO,
          "Your nickname is %s on server %s",
-         conn->nickname, conn->remote_host);
+         conn->local_entry->nickname, conn->remote_host);
     } else {
-      SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_INFO,
-         "Your nickname is %s", conn->nickname);
+      SAY(conn->client, conn, SILC_CLIENT_MESSAGE_INFO,
+         "Your nickname is %s", conn->local_entry->nickname);
     }
 
     COMMAND(SILC_STATUS_OK);
@@ -527,82 +797,68 @@ SILC_CLIENT_CMD_FUNC(nick)
     cmd->argv_lens[1] = 128;
 
   /* Send the NICK command */
-  buffer = silc_command_payload_encode(SILC_COMMAND_NICK, 1,
-                                      &cmd->argv[1],
-                                      &cmd->argv_lens[1],
-                                      &cmd->argv_types[1],
-                                      ++cmd->conn->cmd_ident);
-  silc_client_packet_send(cmd->client, cmd->conn->sock,
-                         SILC_PACKET_COMMAND, NULL, 0, NULL, NULL,
-                         buffer->data, buffer->len, TRUE);
-  silc_buffer_free(buffer);
+  silc_client_command_send_va(conn, cmd, cmd->cmd, NULL, NULL,
+                             1, 1, cmd->argv[1], cmd->argv_lens[1]);
+
+  /* Notify application */
+  COMMAND(SILC_STATUS_OK);
+
+  /** Wait for command reply */
+  silc_fsm_next(fsm, silc_client_command_reply_wait);
+  return SILC_FSM_CONTINUE;
 
  out:
-  silc_client_command_free(cmd);
+  return SILC_FSM_FINISH;
 }
 
+/********************************** LIST ************************************/
+
 /* Command LIST. Lists channels on the current server. */
 
-SILC_CLIENT_CMD_FUNC(list)
+SILC_FSM_STATE(silc_client_command_list)
 {
-  SilcClientCommandContext cmd = (SilcClientCommandContext)context;
+  SilcClientCommandContext cmd = fsm_context;
   SilcClientConnection conn = cmd->conn;
   SilcChannelEntry channel;
-  SilcBuffer buffer, idp = NULL;
-
-  if (!cmd->conn) {
-    SILC_NOT_CONNECTED(cmd->client, cmd->conn);
-    COMMAND_ERROR(SILC_STATUS_ERR_NOT_REGISTERED);
-    goto out;
-  }
+  SilcBuffer idp = NULL;
 
   if (cmd->argc == 2) {
     /* Get the Channel ID of the channel */
-    channel = silc_client_get_channel(cmd->client, cmd->conn, cmd->argv[1]);
+    channel = silc_client_get_channel(conn->client, cmd->conn, cmd->argv[1]);
     if (channel)
       idp = silc_id_payload_encode(channel->id, SILC_ID_CHANNEL);
   }
 
   if (!idp)
-    buffer = silc_command_payload_encode_va(SILC_COMMAND_LIST,
-                                           ++conn->cmd_ident, 0);
+    silc_client_command_send_va(conn, cmd, cmd->cmd, NULL, NULL, 0);
   else
-    buffer = silc_command_payload_encode_va(SILC_COMMAND_LIST,
-                                           ++conn->cmd_ident, 1,
-                                           1, idp->data, idp->len);
+    silc_client_command_send_va(conn, cmd, cmd->cmd, NULL, NULL,
+                               1, 1, silc_buffer_datalen(idp));
 
-  silc_client_packet_send(cmd->client, cmd->conn->sock,
-                         SILC_PACKET_COMMAND, NULL, 0, NULL, NULL,
-                         buffer->data, buffer->len, TRUE);
-  silc_buffer_free(buffer);
-  if (idp)
-    silc_buffer_free(idp);
+  silc_buffer_free(idp);
 
   /* Notify application */
   COMMAND(SILC_STATUS_OK);
 
- out:
-  silc_client_command_free(cmd);
+  /** Wait for command reply */
+  silc_fsm_next(fsm, silc_client_command_reply_wait);
+  return SILC_FSM_CONTINUE;
 }
 
+/********************************** TOPIC ***********************************/
+
 /* Command TOPIC. Sets/shows topic on a channel. */
 
-SILC_CLIENT_CMD_FUNC(topic)
+SILC_FSM_STATE(silc_client_command_topic)
 {
-  SilcClientCommandContext cmd = (SilcClientCommandContext)context;
+  SilcClientCommandContext cmd = fsm_context;
   SilcClientConnection conn = cmd->conn;
   SilcChannelEntry channel;
-  SilcBuffer buffer, idp;
+  SilcBuffer idp;
   char *name;
 
-  if (!cmd->conn) {
-    SILC_NOT_CONNECTED(cmd->client, cmd->conn);
-    COMMAND_ERROR(SILC_STATUS_ERR_NOT_REGISTERED);
-    goto out;
-  }
-
   if (cmd->argc < 2 || cmd->argc > 3) {
-    SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_INFO,
+    SAY(conn->client, conn, SILC_CLIENT_MESSAGE_INFO,
        "Usage: /TOPIC <channel> [<topic>]");
     COMMAND_ERROR((cmd->argc < 2 ? SILC_STATUS_ERR_NOT_ENOUGH_PARAMS :
                   SILC_STATUS_ERR_TOO_MANY_PARAMS));
@@ -625,60 +881,57 @@ SILC_CLIENT_CMD_FUNC(topic)
   }
 
   /* Get the Channel ID of the channel */
-  channel = silc_client_get_channel(cmd->client, conn, name);
+  channel = silc_client_get_channel(conn->client, conn, name);
   if (!channel) {
     COMMAND_ERROR(SILC_STATUS_ERR_NOT_ON_CHANNEL);
     goto out;
   }
 
-  /* Send TOPIC command to the server */
   idp = silc_id_payload_encode(channel->id, SILC_ID_CHANNEL);
+
+  /* Send TOPIC command to the server */
   if (cmd->argc > 2)
-    buffer = silc_command_payload_encode_va(SILC_COMMAND_TOPIC,
-                                           ++conn->cmd_ident, 2,
-                                           1, idp->data, idp->len,
-                                           2, cmd->argv[2],
-                                           strlen(cmd->argv[2]));
+    silc_client_command_send_va(conn, cmd, cmd->cmd, NULL, NULL, 2,
+                               1, silc_buffer_datalen(idp),
+                               2, cmd->argv[2], strlen(cmd->argv[2]));
   else
-    buffer = silc_command_payload_encode_va(SILC_COMMAND_TOPIC,
-                                           ++conn->cmd_ident, 1,
-                                           1, idp->data, idp->len);
-  silc_client_packet_send(cmd->client, conn->sock, SILC_PACKET_COMMAND, NULL,
-                         0, NULL, NULL, buffer->data, buffer->len, TRUE);
-  silc_buffer_free(buffer);
+    silc_client_command_send_va(conn, cmd, cmd->cmd, NULL, NULL, 1,
+                               1, silc_buffer_datalen(idp));
+
   silc_buffer_free(idp);
 
   /* Notify application */
   COMMAND(SILC_STATUS_OK);
 
+  /** Wait for command reply */
+  silc_fsm_next(fsm, silc_client_command_reply_wait);
+  return SILC_FSM_CONTINUE;
+
  out:
-  silc_client_command_free(cmd);
+  return SILC_FSM_FINISH;
 }
 
+/********************************* INVITE ***********************************/
+
 /* Command INVITE. Invites specific client to join a channel. This is
    also used to mange the invite list of the channel. */
 
-SILC_CLIENT_CMD_FUNC(invite)
+SILC_FSM_STATE(silc_client_command_invite)
 {
-  SilcClientCommandContext cmd = (SilcClientCommandContext)context;
-  SilcClient client = cmd->client;
+  SilcClientCommandContext cmd = fsm_context;
   SilcClientConnection conn = cmd->conn;
+  SilcClient client = conn->client;
   SilcClientEntry client_entry = NULL;
   SilcChannelEntry channel;
-  SilcBuffer buffer, clidp, chidp, args = NULL;
+  SilcBuffer clidp, chidp, args = NULL;
   SilcPublicKey pubkey = NULL;
+  SilcDList clients;
   char *nickname = NULL, *name;
   char *invite = NULL;
   unsigned char action[1];
 
-  if (!cmd->conn) {
-    SILC_NOT_CONNECTED(cmd->client, cmd->conn);
-    COMMAND_ERROR(SILC_STATUS_ERR_NOT_REGISTERED);
-    goto out;
-  }
-
   if (cmd->argc < 2) {
-    SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_INFO,
+    SAY(conn->client, conn, SILC_CLIENT_MESSAGE_INFO,
        "Usage: /INVITE <channel> [<nickname>[@server>]"
        "[+|-[<nickname>[@<server>[!<username>[@hostname>]]]]]");
     COMMAND_ERROR(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
@@ -695,7 +948,7 @@ SILC_CLIENT_CMD_FUNC(invite)
   } else {
     name = cmd->argv[1];
 
-    channel = silc_client_get_channel(cmd->client, conn, name);
+    channel = silc_client_get_channel(conn->client, conn, name);
     if (!channel) {
       COMMAND_ERROR(SILC_STATUS_ERR_NOT_ON_CHANNEL);
       goto out;
@@ -711,23 +964,18 @@ SILC_CLIENT_CMD_FUNC(invite)
        nickname = strdup(cmd->argv[2]);
 
       /* Find client entry */
-      client_entry = silc_idlist_get_client(client, conn, nickname,
-                                           cmd->argv[2], TRUE);
-      if (!client_entry) {
-       if (cmd->pending) {
-         COMMAND_ERROR(SILC_STATUS_ERR_NO_SUCH_NICK);
-         goto out;
-       }
-
-       /* Client entry not found, it was requested thus mark this to be
-          pending command. */
-       silc_client_command_pending(conn, SILC_COMMAND_IDENTIFY,
-                                   conn->cmd_ident,
-                                   silc_client_command_invite,
-                                   silc_client_command_dup(cmd));
-       cmd->pending = 1;
-       goto out;
-      }
+      clients = silc_client_get_clients_local(client, conn, nickname,
+                                             cmd->argv[2]);
+      if (!clients)
+       /* Resolve client information */
+       SILC_FSM_CALL(silc_client_get_clients(
+                                     client, conn, nickname,
+                                     cmd->argv[2],
+                                     silc_client_command_resolve_continue,
+                                     cmd));
+
+      client_entry = silc_dlist_get(clients);
+      silc_dlist_uninit(clients);
     } else {
       if (cmd->argv[2][0] == '+')
        action[0] = 0x00;
@@ -735,10 +983,7 @@ SILC_CLIENT_CMD_FUNC(invite)
        action[0] = 0x01;
 
       /* Check if it is public key file to be added to invite list */
-      if (!silc_pkcs_load_public_key(cmd->argv[2] + 1, &pubkey,
-                                    SILC_PKCS_FILE_PEM))
-       silc_pkcs_load_public_key(cmd->argv[2] + 1, &pubkey,
-                                 SILC_PKCS_FILE_BIN);
+      silc_pkcs_load_public_key(cmd->argv[2] + 1, &pubkey);
       invite = cmd->argv[2];
       if (!pubkey)
        invite++;
@@ -751,9 +996,9 @@ SILC_CLIENT_CMD_FUNC(invite)
                       SILC_STR_UI_SHORT(1),
                       SILC_STR_END);
     if (pubkey) {
-      chidp = silc_pkcs_public_key_payload_encode(pubkey);
-      args = silc_argument_payload_encode_one(args, chidp->data,
-                                             chidp->len, 2);
+      chidp = silc_public_key_payload_encode(pubkey);
+      args = silc_argument_payload_encode_one(args, silc_buffer_data(chidp),
+                                             silc_buffer_len(chidp), 2);
       silc_buffer_free(chidp);
       silc_pkcs_public_key_free(pubkey);
     } else {
@@ -764,175 +1009,99 @@ SILC_CLIENT_CMD_FUNC(invite)
   /* Send the command */
   chidp = silc_id_payload_encode(channel->id, SILC_ID_CHANNEL);
   if (client_entry) {
-    clidp = silc_id_payload_encode(client_entry->id, SILC_ID_CLIENT);
-    buffer = silc_command_payload_encode_va(SILC_COMMAND_INVITE,
-                                           ++conn->cmd_ident, 4,
-                                           1, chidp->data, chidp->len,
-                                           2, clidp->data, clidp->len,
-                                           3, args ? action : NULL,
-                                           args ? 1 : 0,
-                                           4, args ? args->data : NULL,
-                                           args ? args->len : 0);
+    clidp = silc_id_payload_encode(&client_entry->id, SILC_ID_CLIENT);
+    silc_client_command_send_va(conn, cmd, cmd->cmd, NULL, NULL, 4,
+                               1, silc_buffer_datalen(chidp),
+                               2, silc_buffer_datalen(clidp),
+                               3, args ? action : NULL, args ? 1 : 0,
+                               4, silc_buffer_datalen(args));
     silc_buffer_free(clidp);
   } else {
-    buffer = silc_command_payload_encode_va(SILC_COMMAND_INVITE,
-                                           ++conn->cmd_ident, 3,
-                                           1, chidp->data, chidp->len,
-                                           3, args ? action : NULL,
-                                           args ? 1 : 0,
-                                           4, args ? args->data : NULL,
-                                           args ? args->len : 0);
+    silc_client_command_send_va(conn, cmd, cmd->cmd, NULL, NULL, 3,
+                               1, silc_buffer_datalen(chidp),
+                               3, args ? action : NULL, args ? 1 : 0,
+                               4, silc_buffer_datalen(args));
   }
 
-  silc_client_packet_send(cmd->client, conn->sock, SILC_PACKET_COMMAND, NULL,
-                         0, NULL, NULL, buffer->data, buffer->len, TRUE);
-  silc_buffer_free(buffer);
   silc_buffer_free(chidp);
   silc_buffer_free(args);
+  silc_free(nickname);
 
   /* Notify application */
   COMMAND(SILC_STATUS_OK);
 
+  /** Wait for command reply */
+  silc_fsm_next(fsm, silc_client_command_reply_wait);
+  return SILC_FSM_CONTINUE;
+
  out:
   silc_free(nickname);
-  silc_client_command_free(cmd);
+  return SILC_FSM_FINISH;
 }
 
-typedef struct {
-  SilcClient client;
-  SilcClientConnection conn;
-} *QuitInternal;
+/********************************** QUIT ************************************/
 
-SILC_TASK_CALLBACK(silc_client_command_quit_cb)
+/* Close the connection */
+
+SILC_FSM_STATE(silc_client_command_quit_final)
 {
-  QuitInternal q = (QuitInternal)context;
+  SilcClientCommandContext cmd = fsm_context;
+  SilcClientConnection conn = cmd->conn;
+  SilcClient client = conn->client;
+
+  /* Notify application */
+  COMMAND(SILC_STATUS_OK);
 
   /* Close connection */
-  q->client->internal->ops->disconnected(q->client, q->conn, 0, NULL);
-  silc_client_close_connection(q->client, q->conn->sock->user_data);
+  conn->callback(client, conn, SILC_CLIENT_CONN_DISCONNECTED,
+                conn->context);
+  //  silc_client_close_connection(q->client, q->conn->sock->user_data);
 
-  silc_free(q);
+  return SILC_FSM_FINISH;
 }
 
 /* Command QUIT. Closes connection with current server. */
 
-SILC_CLIENT_CMD_FUNC(quit)
+SILC_FSM_STATE(silc_client_command_quit)
 {
-  SilcClientCommandContext cmd = (SilcClientCommandContext)context;
-  SilcBuffer buffer;
-  QuitInternal q;
-
-  if (!cmd->conn) {
-    SILC_NOT_CONNECTED(cmd->client, cmd->conn);
-    COMMAND_ERROR(SILC_STATUS_ERR_NOT_REGISTERED);
-    goto out;
-  }
+  SilcClientCommandContext cmd = fsm_context;
+  SilcClientConnection conn = cmd->conn;
 
   if (cmd->argc > 1)
-    buffer = silc_command_payload_encode(SILC_COMMAND_QUIT, cmd->argc - 1,
-                                        &cmd->argv[1], &cmd->argv_lens[1],
-                                        &cmd->argv_types[1],
-                                        ++cmd->conn->cmd_ident);
+    silc_client_command_send_va(conn, cmd, cmd->cmd, NULL, NULL, 1,
+                               1, cmd->argv[1], cmd->argv_lens[1]);
   else
-    buffer = silc_command_payload_encode(SILC_COMMAND_QUIT, 0,
-                                        NULL, NULL, NULL,
-                                        ++cmd->conn->cmd_ident);
-  silc_client_packet_send(cmd->client, cmd->conn->sock, SILC_PACKET_COMMAND,
-                         NULL, 0, NULL, NULL,
-                         buffer->data, buffer->len, TRUE);
-  silc_buffer_free(buffer);
-
-  q = silc_calloc(1, sizeof(*q));
-  q->client = cmd->client;
-  q->conn = cmd->conn;
+    silc_client_command_send_va(conn, cmd, cmd->cmd, NULL, NULL, 0);
 
   /* Sleep for a while */
-  sleep(2);
-
-  /* We quit the connection with little timeout */
-  silc_schedule_task_add(cmd->client->schedule, cmd->conn->sock->sock,
-                        silc_client_command_quit_cb, (void *)q,
-                        1, 0, SILC_TASK_TIMEOUT, SILC_TASK_PRI_NORMAL);
-
-  /* Notify application */
-  COMMAND(SILC_STATUS_OK);
+  sleep(1);
 
- out:
-  silc_client_command_free(cmd);
+  /* We close the connection with a little timeout */
+  silc_fsm_next_later(fsm, silc_client_command_quit_final, 2, 0);
+  return SILC_FSM_WAIT;
 }
 
-/* Timeout callback to remove the killed client from cache */
-
-SILC_TASK_CALLBACK(silc_client_command_kill_remove_later)
-{
-  SilcClientCommandContext cmd = (SilcClientCommandContext)context;
-  SilcClient client = cmd->client;
-  SilcClientConnection conn = cmd->conn;
-  SilcClientEntry target;
-  char *nickname = NULL;
-
-  /* Parse the typed nickname. */
-  if (client->internal->params->nickname_parse)
-    client->internal->params->nickname_parse(cmd->argv[1], &nickname);
-  else
-    nickname = strdup(cmd->argv[1]);
-
-  /* Get the target client */
-  target = silc_idlist_get_client(cmd->client, conn, nickname,
-                                 cmd->argv[1], FALSE);
-  if (target)
-    /* Remove the client from all channels and free it */
-    silc_client_del_client(client, conn, target);
-
-  silc_free(nickname);
-  silc_client_command_free(cmd);
-}
+/********************************** KILL ************************************/
 
-/* Kill command's pending command callback to actually remove the killed
-   client from our local cache. */
-
-SILC_CLIENT_CMD_FUNC(kill_remove)
-{
-  SilcClientCommandContext cmd = (SilcClientCommandContext)context;
-  SilcClientCommandReplyContext reply =
-    (SilcClientCommandReplyContext)context2;
-  SilcStatus status;
-
-  silc_command_get_status(reply->payload, &status, NULL);
-  if (status == SILC_STATUS_OK) {
-    /* Remove with timeout */
-    silc_schedule_task_add(cmd->client->schedule, cmd->conn->sock->sock,
-                          silc_client_command_kill_remove_later, context,
-                          1, 0, SILC_TASK_TIMEOUT, SILC_TASK_PRI_NORMAL);
-    return;
-  }
-
-  silc_client_command_free(cmd);
-}
 
 /* Command KILL. Router operator can use this command to remove an client
    fromthe SILC Network. */
 
-SILC_CLIENT_CMD_FUNC(kill)
+SILC_FSM_STATE(silc_client_command_kill)
 {
-  SilcClientCommandContext cmd = (SilcClientCommandContext)context;
-  SilcClient client = cmd->client;
+  SilcClientCommandContext cmd = fsm_context;
   SilcClientConnection conn = cmd->conn;
-  SilcBuffer buffer, idp, auth = NULL;
+  SilcClient client = conn->client;
+  SilcBuffer idp, auth = NULL;
   SilcClientEntry target;
+  SilcDList clients;
   char *nickname = NULL, *comment = NULL;
 
-  if (!cmd->conn) {
-    SILC_NOT_CONNECTED(cmd->client, cmd->conn);
-    COMMAND_ERROR(SILC_STATUS_ERR_NOT_REGISTERED);
-    goto out;
-  }
-
   if (cmd->argc < 2) {
-    SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_INFO,
+    SAY(conn->client, conn, SILC_CLIENT_MESSAGE_INFO,
        "Usage: /KILL <nickname> [<comment>] [-pubkey]");
     COMMAND_ERROR(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
-    goto out;
+    return SILC_FSM_FINISH;
   }
 
   /* Parse the typed nickname. */
@@ -940,25 +1109,21 @@ SILC_CLIENT_CMD_FUNC(kill)
     client->internal->params->nickname_parse(cmd->argv[1], &nickname);
   else
     nickname = strdup(cmd->argv[1]);
+  if (!nickname)
+    return SILC_FSM_FINISH;
 
   /* Get the target client */
-  target = silc_idlist_get_client(cmd->client, conn, nickname,
-                                 cmd->argv[1], TRUE);
-  if (!target) {
-    if (cmd->pending) {
-      COMMAND_ERROR(SILC_STATUS_ERR_NO_SUCH_NICK);
-      goto out;
-    }
-
-    /* Client entry not found, it was requested thus mark this to be
-       pending command. */
-    silc_client_command_pending(conn, SILC_COMMAND_IDENTIFY,
-                               conn->cmd_ident,
-                               silc_client_command_kill,
-                               silc_client_command_dup(cmd));
-    cmd->pending = 1;
-    goto out;
-  }
+  clients = silc_client_get_clients_local(client, conn, nickname,
+                                         cmd->argv[1]);
+  if (!clients)
+    /* Resolve client information */
+    SILC_FSM_CALL(silc_client_get_clients(client, conn, nickname,
+                                         cmd->argv[1],
+                                         silc_client_command_resolve_continue,
+                                         cmd));
+
+  target = silc_dlist_get(clients);
+  silc_dlist_uninit(clients);
 
   if (cmd->argc >= 3) {
     if (strcasecmp(cmd->argv[2], "-pubkey"))
@@ -967,207 +1132,135 @@ SILC_CLIENT_CMD_FUNC(kill)
     if (!strcasecmp(cmd->argv[2], "-pubkey") ||
        (cmd->argc >= 4 && !strcasecmp(cmd->argv[3], "-pubkey"))) {
       /* Encode the public key authentication payload */
-      auth = silc_auth_public_key_auth_generate(cmd->client->public_key,
-                                               cmd->client->private_key,
-                                               cmd->client->rng,
+      auth = silc_auth_public_key_auth_generate(conn->public_key,
+                                               conn->private_key,
+                                               conn->client->rng,
                                                client->sha1hash,
-                                               target->id, SILC_ID_CLIENT);
+                                               &target->id, SILC_ID_CLIENT);
     }
   }
 
   /* Send the KILL command to the server */
-  idp = silc_id_payload_encode(target->id, SILC_ID_CLIENT);
-  buffer =
-    silc_command_payload_encode_va(SILC_COMMAND_KILL,
-                                  ++conn->cmd_ident, 3,
-                                  1, idp->data, idp->len,
-                                  2, comment, comment ? strlen(comment) : 0,
-                                  3, auth ? auth->data : NULL,
-                                  auth ? auth->len : 0);
-  silc_client_packet_send(cmd->client, conn->sock, SILC_PACKET_COMMAND, NULL,
-                         0, NULL, NULL, buffer->data, buffer->len, TRUE);
-  silc_buffer_free(buffer);
+  idp = silc_id_payload_encode(&target->id, SILC_ID_CLIENT);
+  silc_client_command_send_va(conn, cmd, cmd->cmd, NULL, NULL, 3,
+                             1, silc_buffer_datalen(idp),
+                             2, comment, comment ? strlen(comment) : 0,
+                             3, silc_buffer_datalen(auth));
   silc_buffer_free(idp);
   silc_buffer_free(auth);
+  silc_free(nickname);
 
   /* Notify application */
   COMMAND(SILC_STATUS_OK);
 
-  /* Register a pending callback that will actually remove the killed
-     client from our cache. */
-  silc_client_command_pending(conn, SILC_COMMAND_KILL, conn->cmd_ident,
-                             silc_client_command_kill_remove,
-                             silc_client_command_dup(cmd));
-
- out:
-  silc_free(nickname);
-  silc_client_command_free(cmd);
+  /** Wait for command reply */
+  silc_fsm_next(fsm, silc_client_command_reply_wait);
+  return SILC_FSM_CONTINUE;
 }
 
+/********************************** INFO ************************************/
+
 /* Command INFO. Request information about specific server. If specific
    server is not provided the current server is used. */
 
-SILC_CLIENT_CMD_FUNC(info)
+SILC_FSM_STATE(silc_client_command_info)
 {
-  SilcClientCommandContext cmd = (SilcClientCommandContext)context;
+  SilcClientCommandContext cmd = fsm_context;
   SilcClientConnection conn = cmd->conn;
-  SilcBuffer buffer;
-  char *name = NULL;
-
-  if (!cmd->conn) {
-    SILC_NOT_CONNECTED(cmd->client, cmd->conn);
-    COMMAND_ERROR(SILC_STATUS_ERR_NOT_REGISTERED);
-    goto out;
-  }
-
-  if (cmd->argc == 2)
-    name = strdup(cmd->argv[1]);
 
   /* Send the command */
-  if (name)
-    buffer = silc_command_payload_encode_va(SILC_COMMAND_INFO,
-                                           ++conn->cmd_ident, 1,
-                                           1, name, strlen(name));
+  if (cmd->argc == 2)
+    silc_client_command_send_va(conn, cmd, cmd->cmd, NULL, NULL, 1,
+                               1, cmd->argv[1], cmd->argv_lens[1]);
   else
-    buffer = silc_command_payload_encode(SILC_COMMAND_INFO, 0,
-                                        NULL, NULL, NULL, ++conn->cmd_ident);
-  silc_client_packet_send(cmd->client, conn->sock, SILC_PACKET_COMMAND, NULL,
-                         0, NULL, NULL, buffer->data, buffer->len, TRUE);
-  silc_buffer_free(buffer);
-  if (name)
-    silc_free(name);
+    silc_client_command_send_va(conn, cmd, cmd->cmd, NULL, NULL, 0);
 
   /* Notify application */
   COMMAND(SILC_STATUS_OK);
 
- out:
-  silc_client_command_free(cmd);
+  /** Wait for command reply */
+  silc_fsm_next(fsm, silc_client_command_reply_wait);
+  return SILC_FSM_CONTINUE;
 }
 
+/********************************** STATS ***********************************/
+
 /* Command STATS. Shows server and network statistics. */
 
-SILC_CLIENT_CMD_FUNC(stats)
+SILC_FSM_STATE(silc_client_command_stats)
 {
-  SilcClientCommandContext cmd = (SilcClientCommandContext)context;
+  SilcClientCommandContext cmd = fsm_context;
   SilcClientConnection conn = cmd->conn;
-  SilcBuffer buffer, idp = NULL;
 
-  if (!cmd->conn) {
-    SILC_NOT_CONNECTED(cmd->client, cmd->conn);
-    COMMAND_ERROR(SILC_STATUS_ERR_NOT_REGISTERED);
-    goto out;
+  if (cmd->argc < 2) {
+    COMMAND_ERROR(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
+    return SILC_FSM_FINISH;
   }
 
-  idp = silc_id_payload_encode(conn->remote_id, SILC_ID_SERVER);
-
   /* Send the command */
-  buffer = silc_command_payload_encode_va(SILC_COMMAND_STATS,
-                                         ++conn->cmd_ident, 1,
-                                         SILC_ID_SERVER, idp->data, idp->len);
-  silc_client_packet_send(cmd->client, conn->sock, SILC_PACKET_COMMAND, NULL,
-                         0, NULL, NULL, buffer->data, buffer->len, TRUE);
-  silc_buffer_free(buffer);
-  silc_buffer_free(idp);
+  silc_client_command_send_va(conn, cmd, cmd->cmd, NULL, NULL, 1,
+                             1, silc_buffer_datalen(conn->remote_idp));
 
   /* Notify application */
   COMMAND(SILC_STATUS_OK);
 
- out:
-  silc_client_command_free(cmd);
+  /** Wait for command reply */
+  silc_fsm_next(fsm, silc_client_command_reply_wait);
+  return SILC_FSM_CONTINUE;
 }
 
-/* Command PING. Sends ping to server. This is used to test the
-   communication channel. */
+/********************************** PING ************************************/
 
-SILC_CLIENT_CMD_FUNC(ping)
+/* Command PING. Sends ping to server. */
+
+SILC_FSM_STATE(silc_client_command_ping)
 {
-  SilcClientCommandContext cmd = (SilcClientCommandContext)context;
+  SilcClientCommandContext cmd = fsm_context;
   SilcClientConnection conn = cmd->conn;
-  SilcBuffer buffer, idp;
-  void *id;
-  int i;
 
-  if (!cmd->conn) {
-    SILC_NOT_CONNECTED(cmd->client, cmd->conn);
-    COMMAND_ERROR(SILC_STATUS_ERR_NOT_REGISTERED);
-    goto out;
+  if (cmd->argc < 2) {
+    COMMAND_ERROR(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
+    return SILC_FSM_FINISH;
   }
 
-  idp = silc_id_payload_encode(conn->remote_id, SILC_ID_SERVER);
-
   /* Send the command */
-  buffer = silc_command_payload_encode_va(SILC_COMMAND_PING,
-                                         ++conn->cmd_ident, 1,
-                                         1, idp->data, idp->len);
-  silc_client_packet_send(cmd->client, conn->sock, SILC_PACKET_COMMAND, NULL,
-                         0, NULL, NULL, buffer->data, buffer->len, TRUE);
-  silc_buffer_free(buffer);
-  silc_buffer_free(idp);
+  silc_client_command_send_va(conn, cmd, cmd->cmd, NULL, NULL, 1,
+                             1, silc_buffer_datalen(conn->remote_idp));
 
-  id = silc_id_str2id(conn->remote_id_data, conn->remote_id_data_len,
-                     SILC_ID_SERVER);
-  if (!id) {
-    SILC_NOT_CONNECTED(cmd->client, cmd->conn);
-    COMMAND_ERROR(SILC_STATUS_ERR_NOT_REGISTERED);
-    goto out;
-  }
-
-  /* Start counting time */
-  for (i = 0; i < conn->internal->ping_count; i++) {
-    if (conn->internal->ping[i].dest_id == NULL) {
-      conn->internal->ping[i].start_time = time(NULL);
-      conn->internal->ping[i].dest_id = id;
-      conn->internal->ping[i].dest_name = strdup(conn->remote_host);
-      break;
-    }
-  }
-  if (i >= conn->internal->ping_count) {
-    i = conn->internal->ping_count;
-    conn->internal->ping =
-      silc_realloc(conn->internal->ping,
-                  sizeof(*conn->internal->ping) * (i + 1));
-    conn->internal->ping[i].start_time = time(NULL);
-    conn->internal->ping[i].dest_id = id;
-    conn->internal->ping[i].dest_name = strdup(conn->remote_host);
-    conn->internal->ping_count++;
-  }
+  /* Save ping time */
+  cmd->context = SILC_64_TO_PTR(silc_time());
 
   /* Notify application */
   COMMAND(SILC_STATUS_OK);
 
- out:
-  silc_client_command_free(cmd);
+  /** Wait for command reply */
+  silc_fsm_next(fsm, silc_client_command_reply_wait);
+  return SILC_FSM_CONTINUE;
 }
 
+/********************************** JOIN ************************************/
+
 /* Command JOIN. Joins to a channel. */
 
-SILC_CLIENT_CMD_FUNC(join)
+SILC_FSM_STATE(silc_client_command_join)
 {
-  SilcClientCommandContext cmd = (SilcClientCommandContext)context;
+  SilcClientCommandContext cmd = fsm_context;
   SilcClientConnection conn = cmd->conn;
   SilcChannelEntry channel;
-  SilcBuffer buffer, idp, auth = NULL, cauth = NULL;
+  SilcBuffer auth = NULL, cauth = NULL;
   char *name, *passphrase = NULL, *pu8, *cipher = NULL, *hmac = NULL;
   int i, passphrase_len = 0;
 
-  if (!cmd->conn) {
-    SILC_NOT_CONNECTED(cmd->client, cmd->conn);
-    COMMAND_ERROR(SILC_STATUS_ERR_NOT_REGISTERED);
-    goto out;
-  }
-
   if (cmd->argc < 2) {
     COMMAND_ERROR(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
     goto out;
   }
 
   /* See if we have joined to the requested channel already */
-  channel = silc_client_get_channel(cmd->client, conn, cmd->argv[1]);
+  channel = silc_client_get_channel(conn->client, conn, cmd->argv[1]);
   if (channel && silc_client_on_channel(channel, conn->local_entry))
     goto out;
 
-  idp = silc_id_payload_encode(conn->local_id, SILC_ID_CLIENT);
-
   if (cmd->argv_lens[1] > 256)
     cmd->argv_lens[1] = 256;
 
@@ -1175,21 +1268,19 @@ SILC_CLIENT_CMD_FUNC(join)
 
   for (i = 2; i < cmd->argc; i++) {
     if (!strcasecmp(cmd->argv[i], "-cipher") && cmd->argc > i + 1) {
-      cipher = cmd->argv[i + 1];
-      i++;
+      cipher = cmd->argv[++i];
     } else if (!strcasecmp(cmd->argv[i], "-hmac") && cmd->argc > i + 1) {
-      hmac = cmd->argv[i + 1];
-      i++;
+      hmac = cmd->argv[++i];
     } else if (!strcasecmp(cmd->argv[i], "-founder")) {
-      auth = silc_auth_public_key_auth_generate(cmd->client->public_key,
-                                               cmd->client->private_key,
-                                               cmd->client->rng,
-                                               cmd->client->sha1hash,
+      auth = silc_auth_public_key_auth_generate(conn->public_key,
+                                               conn->private_key,
+                                               conn->client->rng,
+                                               conn->client->sha1hash,
                                                conn->local_id,
                                                SILC_ID_CLIENT);
     } else if (!strcasecmp(cmd->argv[i], "-auth")) {
-      SilcPublicKey pubkey = cmd->client->public_key;
-      SilcPrivateKey privkey = cmd->client->private_key;
+      SilcPublicKey pubkey = conn->public_key;
+      SilcPrivateKey privkey = conn->private_key;
       unsigned char *pk, pkhash[SILC_HASH_MAXLEN], *pubdata;
       SilcUInt32 pk_len;
 
@@ -1200,8 +1291,8 @@ SILC_CLIENT_CMD_FUNC(join)
          i++;
        }
        if (!silc_load_key_pair(cmd->argv[i + 1], cmd->argv[i + 2], pass,
-                               NULL, &pubkey, &privkey)) {
-         SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_ERROR,
+                               &pubkey, &privkey)) {
+         SAY(conn->client, conn, SILC_CLIENT_MESSAGE_ERROR,
              "Could not load key pair, check your arguments");
          COMMAND_ERROR(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
          goto out;
@@ -1210,13 +1301,13 @@ SILC_CLIENT_CMD_FUNC(join)
       }
 
       pk = silc_pkcs_public_key_encode(pubkey, &pk_len);
-      silc_hash_make(cmd->client->sha1hash, pk, pk_len, pkhash);
+      silc_hash_make(conn->client->sha1hash, pk, pk_len, pkhash);
       silc_free(pk);
-      pubdata = silc_rng_get_rn_data(cmd->client->rng, 128);
+      pubdata = silc_rng_get_rn_data(conn->client->rng, 128);
       memcpy(pubdata, pkhash, 20);
       cauth = silc_auth_public_key_auth_generate_wpub(pubkey, privkey,
                                                      pubdata, 128,
-                                                     cmd->client->sha1hash,
+                                                     conn->client->sha1hash,
                                                      conn->local_id,
                                                      SILC_ID_CLIENT);
       memset(pubdata, 0, 128);
@@ -1238,21 +1329,15 @@ SILC_CLIENT_CMD_FUNC(join)
   }
 
   /* Send JOIN command to the server */
-  buffer =
-    silc_command_payload_encode_va(SILC_COMMAND_JOIN, ++conn->cmd_ident, 7,
-                                  1, name, strlen(name),
-                                  2, idp->data, idp->len,
-                                  3, passphrase, passphrase_len,
-                                  4, cipher, cipher ? strlen(cipher) : 0,
-                                  5, hmac, hmac ? strlen(hmac) : 0,
-                                  6, auth ? auth->data : NULL,
-                                  auth ? auth->len : 0,
-                                  7, cauth ? cauth->data : NULL,
-                                  cauth ? cauth->len : 0);
-  silc_client_packet_send(cmd->client, conn->sock, SILC_PACKET_COMMAND, NULL,
-                         0, NULL, NULL, buffer->data, buffer->len, TRUE);
-  silc_buffer_free(buffer);
-  silc_buffer_free(idp);
+  silc_client_command_send_va(conn, cmd, cmd->cmd, NULL, NULL, 7,
+                             1, name, strlen(name),
+                             2, silc_buffer_datalen(conn->local_idp),
+                             3, passphrase, passphrase_len,
+                             4, cipher, cipher ? strlen(cipher) : 0,
+                             5, hmac, hmac ? strlen(hmac) : 0,
+                             6, silc_buffer_datalen(auth),
+                             7, silc_buffer_datalen(cauth));
+
   silc_buffer_free(auth);
   silc_buffer_free(cauth);
   if (passphrase)
@@ -1262,77 +1347,66 @@ SILC_CLIENT_CMD_FUNC(join)
   /* Notify application */
   COMMAND(SILC_STATUS_OK);
 
+  /** Wait for command reply */
+  silc_fsm_next(fsm, silc_client_command_reply_wait);
+  return SILC_FSM_CONTINUE;
+
  out:
-  silc_client_command_free(cmd);
+  return SILC_FSM_FINISH;
 }
 
+/********************************** MOTD ************************************/
+
 /* MOTD command. Requests motd from server. */
 
-SILC_CLIENT_CMD_FUNC(motd)
+SILC_FSM_STATE(silc_client_command_motd)
 {
-  SilcClientCommandContext cmd = (SilcClientCommandContext)context;
+  SilcClientCommandContext cmd = fsm_context;
   SilcClientConnection conn = cmd->conn;
-  SilcBuffer buffer;
-
-  if (!cmd->conn) {
-    SILC_NOT_CONNECTED(cmd->client, cmd->conn);
-    COMMAND_ERROR(SILC_STATUS_ERR_NOT_REGISTERED);
-    goto out;
-  }
 
   if (cmd->argc < 1 || cmd->argc > 2) {
-    SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_INFO,
+    SAY(conn->client, conn, SILC_CLIENT_MESSAGE_INFO,
        "Usage: /MOTD [<server>]");
     COMMAND_ERROR((cmd->argc < 1 ? SILC_STATUS_ERR_NOT_ENOUGH_PARAMS :
                   SILC_STATUS_ERR_TOO_MANY_PARAMS));
-    goto out;
+    return SILC_FSM_FINISH;
   }
 
-  /* Send TOPIC command to the server */
+  /* Send the command */
   if (cmd->argc == 1)
-    buffer = silc_command_payload_encode_va(SILC_COMMAND_MOTD,
-                                           ++conn->cmd_ident, 1,
-                                           1, conn->remote_host,
-                                           strlen(conn->remote_host));
+    silc_client_command_send_va(conn, cmd, cmd->cmd, NULL, NULL, 1,
+                               1, conn->remote_host,
+                               strlen(conn->remote_host));
   else
-    buffer = silc_command_payload_encode_va(SILC_COMMAND_MOTD,
-                                           ++conn->cmd_ident, 1,
-                                           1, cmd->argv[1],
-                                           cmd->argv_lens[1]);
-  silc_client_packet_send(cmd->client, conn->sock, SILC_PACKET_COMMAND, NULL,
-                         0, NULL, NULL, buffer->data, buffer->len, TRUE);
-  silc_buffer_free(buffer);
+    silc_client_command_send_va(conn, cmd, cmd->cmd, NULL, NULL, 1,
+                               1, cmd->argv[1], cmd->argv_lens[1]);
 
   /* Notify application */
   COMMAND(SILC_STATUS_OK);
 
- out:
-  silc_client_command_free(cmd);
+  /** Wait for command reply */
+  silc_fsm_next(fsm, silc_client_command_reply_wait);
+  return SILC_FSM_CONTINUE;
 }
 
+/********************************** UMODE ***********************************/
+
 /* UMODE. Set/unset user mode in SILC. This is used mainly to unset the
    modes as client cannot set itself server/router operator privileges. */
 
-SILC_CLIENT_CMD_FUNC(umode)
+SILC_FSM_STATE(silc_client_command_umode)
 {
-  SilcClientCommandContext cmd = (SilcClientCommandContext)context;
+  SilcClientCommandContext cmd = fsm_context;
   SilcClientConnection conn = cmd->conn;
-  SilcBuffer buffer, idp;
   unsigned char *cp, modebuf[4];
   SilcUInt32 mode, add, len;
   int i;
 
-  if (!cmd->conn) {
-    SILC_NOT_CONNECTED(cmd->client, cmd->conn);
-    COMMAND_ERROR(SILC_STATUS_ERR_NOT_REGISTERED);
-    goto out;
-  }
-
   if (cmd->argc < 2) {
-    SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_INFO,
+    SAY(conn->client, conn, SILC_CLIENT_MESSAGE_INFO,
        "Usage: /UMODE +|-<modes>");
     COMMAND_ERROR(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
-    goto out;
+    return SILC_FSM_FINISH;
   }
 
   mode = conn->local_entry->mode;
@@ -1433,54 +1507,45 @@ SILC_CLIENT_CMD_FUNC(umode)
       break;
     default:
       COMMAND_ERROR(SILC_STATUS_ERR_UNKNOWN_MODE);
-      goto out;
+      return SILC_FSM_FINISH;
       break;
     }
   }
 
-  idp = silc_id_payload_encode(conn->local_id, SILC_ID_CLIENT);
   SILC_PUT32_MSB(mode, modebuf);
 
-  /* Send the command packet. We support sending only one mode at once
-     that requires an argument. */
-  buffer =
-    silc_command_payload_encode_va(SILC_COMMAND_UMODE, ++conn->cmd_ident, 2,
-                                  1, idp->data, idp->len,
-                                  2, modebuf, sizeof(modebuf));
-  silc_client_packet_send(cmd->client, conn->sock, SILC_PACKET_COMMAND, NULL,
-                         0, NULL, NULL, buffer->data, buffer->len, TRUE);
-  silc_buffer_free(buffer);
-  silc_buffer_free(idp);
+  /* Send the command */
+  silc_client_command_send_va(conn, cmd, cmd->cmd, NULL, NULL, 2,
+                             1, silc_buffer_datalen(conn->local_idp),
+                             2, modebuf, sizeof(modebuf));
 
   /* Notify application */
   COMMAND(SILC_STATUS_OK);
 
- out:
-  silc_client_command_free(cmd);
+  /** Wait for command reply */
+  silc_fsm_next(fsm, silc_client_command_reply_wait);
+  return SILC_FSM_CONTINUE;
 }
 
+/********************************** CMODE ***********************************/
+
 /* CMODE command. Sets channel mode. Modes that does not require any arguments
    can be set several at once. Those modes that require argument must be set
    separately (unless set with modes that does not require arguments). */
 
-SILC_CLIENT_CMD_FUNC(cmode)
+SILC_FSM_STATE(silc_client_command_cmode)
 {
-  SilcClientCommandContext cmd = (SilcClientCommandContext)context;
+  SilcClientCommandContext cmd = fsm_context;
   SilcClientConnection conn = cmd->conn;
+  SilcClient client = conn->client;
   SilcChannelEntry channel;
-  SilcBuffer buffer, chidp, auth = NULL, pk = NULL;
+  SilcBuffer chidp, auth = NULL, pk = NULL;
   unsigned char *name, *cp, modebuf[4], tmp[4], *arg = NULL;
   SilcUInt32 mode, add, type, len, arg_len = 0;
   int i;
 
-  if (!cmd->conn) {
-    SILC_NOT_CONNECTED(cmd->client, cmd->conn);
-    COMMAND_ERROR(SILC_STATUS_ERR_NOT_REGISTERED);
-    goto out;
-  }
-
   if (cmd->argc < 3) {
-    SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_INFO,
+    SAY(conn->client, conn, SILC_CLIENT_MESSAGE_INFO,
        "Usage: /CMODE <channel> +|-<modes> [{ <arguments>}]");
     COMMAND_ERROR(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
     goto out;
@@ -1496,7 +1561,7 @@ SILC_CLIENT_CMD_FUNC(cmode)
   } else {
     name = cmd->argv[1];
 
-    channel = silc_client_get_channel(cmd->client, conn, name);
+    channel = silc_client_get_channel(conn->client, conn, name);
     if (!channel) {
       COMMAND_ERROR(SILC_STATUS_ERR_NOT_ON_CHANNEL);
       goto out;
@@ -1567,7 +1632,7 @@ SILC_CLIENT_CMD_FUNC(cmode)
        mode |= SILC_CHANNEL_MODE_ULIMIT;
        type = 3;
        if (cmd->argc < 4) {
-         SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_INFO,
+         SAY(conn->client, conn, SILC_CLIENT_MESSAGE_INFO,
              "Usage: /CMODE <channel> +|-<modes> [{ <arguments>}]");
          COMMAND_ERROR(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
          goto out;
@@ -1585,7 +1650,7 @@ SILC_CLIENT_CMD_FUNC(cmode)
        mode |= SILC_CHANNEL_MODE_PASSPHRASE;
        type = 4;
        if (cmd->argc < 4) {
-         SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_INFO,
+         SAY(conn->client, conn, SILC_CLIENT_MESSAGE_INFO,
              "Usage: /CMODE <channel> +|-<modes> [{ <arguments>}]");
          COMMAND_ERROR(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
          goto out;
@@ -1601,7 +1666,7 @@ SILC_CLIENT_CMD_FUNC(cmode)
        mode |= SILC_CHANNEL_MODE_CIPHER;
        type = 5;
        if (cmd->argc < 4) {
-         SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_INFO,
+         SAY(conn->client, conn, SILC_CLIENT_MESSAGE_INFO,
              "Usage: /CMODE <channel> +|-<modes> [{ <arguments>}]");
          COMMAND_ERROR(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
          goto out;
@@ -1617,7 +1682,7 @@ SILC_CLIENT_CMD_FUNC(cmode)
        mode |= SILC_CHANNEL_MODE_HMAC;
        type = 6;
        if (cmd->argc < 4) {
-         SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_INFO,
+         SAY(conn->client, conn, SILC_CLIENT_MESSAGE_INFO,
              "Usage: /CMODE <channel> +|-<modes> [{ <arguments>}]");
          COMMAND_ERROR(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
          goto out;
@@ -1630,8 +1695,8 @@ SILC_CLIENT_CMD_FUNC(cmode)
       break;
     case 'f':
       if (add) {
-       SilcPublicKey pubkey = cmd->client->public_key;
-       SilcPrivateKey privkey = cmd->client->private_key;
+       SilcPublicKey pubkey = conn->public_key;
+       SilcPrivateKey privkey = conn->private_key;
 
        mode |= SILC_CHANNEL_MODE_FOUNDER_AUTH;
        type = 7;
@@ -1641,22 +1706,22 @@ SILC_CLIENT_CMD_FUNC(cmode)
          if (cmd->argc >= 6)
            pass = cmd->argv[5];
          if (!silc_load_key_pair(cmd->argv[3], cmd->argv[4], pass,
-                                 NULL, &pubkey, &privkey)) {
-           SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_ERROR,
+                                 &pubkey, &privkey)) {
+           SAY(client, conn, SILC_CLIENT_MESSAGE_ERROR,
                "Could not load key pair, check your arguments");
            COMMAND_ERROR(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
            goto out;
          }
        }
 
-       pk = silc_pkcs_public_key_payload_encode(pubkey);
+       pk = silc_public_key_payload_encode(pubkey);
        auth = silc_auth_public_key_auth_generate(pubkey, privkey,
-                                                 cmd->client->rng,
-                                                 cmd->client->sha1hash,
+                                                 conn->client->rng,
+                                                 conn->client->sha1hash,
                                                  conn->local_id,
                                                  SILC_ID_CLIENT);
-       arg = auth->data;
-       arg_len = auth->len;
+       arg = silc_buffer_data(auth);
+       arg_len = silc_buffer_len(auth);
       } else {
        mode &= ~SILC_CHANNEL_MODE_FOUNDER_AUTH;
       }
@@ -1673,8 +1738,9 @@ SILC_CLIENT_CMD_FUNC(cmode)
        if (cmd->argc == 3) {
          /* Send empty command to receive the public key list. */
          chidp = silc_id_payload_encode(channel->id, SILC_ID_CHANNEL);
-         silc_client_command_send(cmd->client, conn, SILC_COMMAND_CMODE,
-                                  0, 1, 1, chidp->data, chidp->len);
+         silc_client_command_send_va(conn, cmd, SILC_COMMAND_CMODE,
+                                     NULL, NULL, 1,
+                                     1, silc_buffer_datalen(chidp));
          silc_buffer_free(chidp);
 
          /* Notify application */
@@ -1692,21 +1758,19 @@ SILC_CLIENT_CMD_FUNC(cmode)
        for (k = 3; k < cmd->argc; k++) {
          if (cmd->argv[k][0] == '+')
            chadd = TRUE;
-         if (!silc_pkcs_load_public_key(cmd->argv[k] + 1, &chpk,
-                                        SILC_PKCS_FILE_PEM))
-           if (!silc_pkcs_load_public_key(cmd->argv[k] + 1, &chpk,
-                                          SILC_PKCS_FILE_BIN)) {
-             SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_ERROR,
-                 "Could not load public key %s, check the filename",
-                 cmd->argv[k]);
-             COMMAND_ERROR(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
-             silc_buffer_free(auth);
-             goto out;
-           }
+         if (!silc_pkcs_load_public_key(cmd->argv[k] + 1, &chpk)) {
+           SAY(conn->client, conn, SILC_CLIENT_MESSAGE_ERROR,
+               "Could not load public key %s, check the filename",
+               cmd->argv[k]);
+           COMMAND_ERROR(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
+           silc_buffer_free(auth);
+           goto out;
+         }
 
          if (chpk) {
-           pk = silc_pkcs_public_key_payload_encode(chpk);
-           auth = silc_argument_payload_encode_one(auth, pk->data, pk->len,
+           pk = silc_public_key_payload_encode(chpk);
+           auth = silc_argument_payload_encode_one(auth,
+                                                   silc_buffer_datalen(pk),
                                                    chadd ? 0x00 : 0x01);
            silc_pkcs_public_key_free(chpk);
            silc_buffer_free(pk);
@@ -1714,8 +1778,8 @@ SILC_CLIENT_CMD_FUNC(cmode)
          }
        }
 
-       arg = auth->data;
-       arg_len = auth->len;
+       arg = silc_buffer_data(auth);
+       arg_len = silc_buffer_len(auth);
       } else {
        mode &= ~SILC_CHANNEL_MODE_CHANNEL_AUTH;
       }
@@ -1730,26 +1794,20 @@ SILC_CLIENT_CMD_FUNC(cmode)
   chidp = silc_id_payload_encode(channel->id, SILC_ID_CHANNEL);
   SILC_PUT32_MSB(mode, modebuf);
 
-  /* Send the command packet. We support sending only one mode at once
-     that requires an argument. */
+  /* Send the command. We support sending only one mode at once that
+     requires an argument. */
   if (type && arg) {
-    buffer =
-      silc_command_payload_encode_va(SILC_COMMAND_CMODE, ++conn->cmd_ident, 4,
-                                    1, chidp->data, chidp->len,
-                                    2, modebuf, sizeof(modebuf),
-                                    type, arg, arg_len,
-                                    8, pk ? pk->data : NULL,
-                                    pk ? pk->len : 0);
+    silc_client_command_send_va(conn, cmd, cmd->cmd, NULL, NULL, 4,
+                               1, silc_buffer_datalen(chidp),
+                               2, modebuf, sizeof(modebuf),
+                               type, arg, arg_len,
+                               8, silc_buffer_datalen(pk));
   } else {
-    buffer =
-      silc_command_payload_encode_va(SILC_COMMAND_CMODE, ++conn->cmd_ident, 2,
-                                    1, chidp->data, chidp->len,
-                                    2, modebuf, sizeof(modebuf));
+    silc_client_command_send_va(conn, cmd, cmd->cmd, NULL, NULL, 2,
+                               1, silc_buffer_datalen(chidp),
+                               2, modebuf, sizeof(modebuf));
   }
 
-  silc_client_packet_send(cmd->client, conn->sock, SILC_PACKET_COMMAND, NULL,
-                         0, NULL, NULL, buffer->data, buffer->len, TRUE);
-  silc_buffer_free(buffer);
   silc_buffer_free(chidp);
   silc_buffer_free(auth);
   silc_buffer_free(pk);
@@ -1757,34 +1815,35 @@ SILC_CLIENT_CMD_FUNC(cmode)
   /* Notify application */
   COMMAND(SILC_STATUS_OK);
 
+  /** Wait for command reply */
+  silc_fsm_next(fsm, silc_client_command_reply_wait);
+  return SILC_FSM_CONTINUE;
+
  out:
-  silc_client_command_free(cmd);
+  return SILC_FSM_FINISH;
 }
 
+/********************************* CUMODE ***********************************/
+
 /* CUMODE command. Changes client's mode on a channel. */
 
-SILC_CLIENT_CMD_FUNC(cumode)
+SILC_FSM_STATE(silc_client_command_cumode)
 {
-  SilcClientCommandContext cmd = (SilcClientCommandContext)context;
-  SilcClient client = cmd->client;
+  SilcClientCommandContext cmd = fsm_context;
   SilcClientConnection conn = cmd->conn;
+  SilcClient client = conn->client;
   SilcChannelEntry channel;
   SilcChannelUser chu;
   SilcClientEntry client_entry;
-  SilcBuffer buffer, clidp, chidp, auth = NULL;
+  SilcBuffer clidp, chidp, auth = NULL;
+  SilcDList clients;
   unsigned char *name, *cp, modebuf[4];
   SilcUInt32 mode = 0, add, len;
   char *nickname = NULL;
   int i;
 
-  if (!cmd->conn) {
-    SILC_NOT_CONNECTED(cmd->client, cmd->conn);
-    COMMAND_ERROR(SILC_STATUS_ERR_NOT_REGISTERED);
-    goto out;
-  }
-
   if (cmd->argc < 4) {
-    SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_INFO,
+    SAY(conn->client, conn, SILC_CLIENT_MESSAGE_INFO,
        "Usage: /CUMODE <channel> +|-<modes> <nickname>[@<server>]");
     COMMAND_ERROR(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
     goto out;
@@ -1800,7 +1859,7 @@ SILC_CLIENT_CMD_FUNC(cumode)
   } else {
     name = cmd->argv[1];
 
-    channel = silc_client_get_channel(cmd->client, conn, name);
+    channel = silc_client_get_channel(conn->client, conn, name);
     if (!channel) {
       COMMAND_ERROR(SILC_STATUS_ERR_NOT_ON_CHANNEL);
       goto out;
@@ -1814,23 +1873,16 @@ SILC_CLIENT_CMD_FUNC(cumode)
     nickname = strdup(cmd->argv[3]);
 
   /* Find client entry */
-  client_entry = silc_idlist_get_client(cmd->client, conn, nickname,
-                                       cmd->argv[3], TRUE);
-  if (!client_entry) {
-    if (cmd->pending) {
-      COMMAND_ERROR(SILC_STATUS_ERR_NO_SUCH_NICK);
-      goto out;
-    }
+  clients = silc_client_get_clients_local(client, conn, nickname,
+                                         cmd->argv[3]);
+  if (!clients)
+    /* Resolve client information */
+    SILC_FSM_CALL(silc_client_get_clients(client, conn, nickname, cmd->argv[3],
+                                         silc_client_command_resolve_continue,
+                                         cmd));
 
-    /* Client entry not found, it was requested thus mark this to be
-       pending command. */
-    silc_client_command_pending(conn, SILC_COMMAND_IDENTIFY,
-                               conn->cmd_ident,
-                               silc_client_command_cumode,
-                               silc_client_command_dup(cmd));
-    cmd->pending = 1;
-    goto out;
-  }
+  client_entry = silc_dlist_get(clients);
+  silc_dlist_uninit(clients);
 
   /* Get the current mode */
   chu = silc_client_on_channel(channel, client_entry);
@@ -1861,16 +1913,16 @@ SILC_CLIENT_CMD_FUNC(cumode)
       break;
     case 'f':
       if (add) {
-       SilcPublicKey pubkey = cmd->client->public_key;
-       SilcPrivateKey privkey = cmd->client->private_key;
+       SilcPublicKey pubkey = conn->public_key;
+       SilcPrivateKey privkey = conn->private_key;
 
        if (cmd->argc >= 6) {
          char *pass = "";
          if (cmd->argc >= 7)
            pass = cmd->argv[6];
          if (!silc_load_key_pair(cmd->argv[4], cmd->argv[5], pass,
-                                 NULL, &pubkey, &privkey)) {
-           SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_ERROR,
+                                 &pubkey, &privkey)) {
+           SAY(conn->client, conn, SILC_CLIENT_MESSAGE_ERROR,
                "Could not load key pair, check your arguments");
            COMMAND_ERROR(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
            goto out;
@@ -1878,8 +1930,8 @@ SILC_CLIENT_CMD_FUNC(cumode)
        }
 
        auth = silc_auth_public_key_auth_generate(pubkey, privkey,
-                                                 cmd->client->rng,
-                                                 cmd->client->sha1hash,
+                                                 conn->client->rng,
+                                                 conn->client->sha1hash,
                                                  conn->local_id,
                                                  SILC_ID_CLIENT);
        mode |= SILC_CHANNEL_UMODE_CHANFO;
@@ -1926,22 +1978,16 @@ SILC_CLIENT_CMD_FUNC(cumode)
 
   chidp = silc_id_payload_encode(channel->id, SILC_ID_CHANNEL);
   SILC_PUT32_MSB(mode, modebuf);
-  clidp = silc_id_payload_encode(client_entry->id, SILC_ID_CLIENT);
+  clidp = silc_id_payload_encode(&client_entry->id, SILC_ID_CLIENT);
 
   /* Send the command packet. We support sending only one mode at once
      that requires an argument. */
-  buffer = silc_command_payload_encode_va(SILC_COMMAND_CUMODE,
-                                         ++conn->cmd_ident,
-                                         auth ? 4 : 3,
-                                         1, chidp->data, chidp->len,
-                                         2, modebuf, 4,
-                                         3, clidp->data, clidp->len,
-                                         4, auth ? auth->data : NULL,
-                                         auth ? auth->len : 0);
-
-  silc_client_packet_send(cmd->client, conn->sock, SILC_PACKET_COMMAND, NULL,
-                         0, NULL, NULL, buffer->data, buffer->len, TRUE);
-  silc_buffer_free(buffer);
+  silc_client_command_send_va(conn, cmd, cmd->cmd, NULL, NULL, auth ? 4 : 3,
+                             1, silc_buffer_datalen(chidp),
+                             2, modebuf, 4,
+                             3, silc_buffer_datalen(clidp),
+                             4, silc_buffer_datalen(auth));
+
   silc_buffer_free(chidp);
   silc_buffer_free(clidp);
   if (auth)
@@ -1952,30 +1998,27 @@ SILC_CLIENT_CMD_FUNC(cumode)
 
  out:
   silc_free(nickname);
-  silc_client_command_free(cmd);
+  return SILC_FSM_FINISH;
 }
 
+/********************************** KICK ************************************/
+
 /* KICK command. Kicks a client out of channel. */
 
-SILC_CLIENT_CMD_FUNC(kick)
+SILC_FSM_STATE(silc_client_command_kick)
 {
-  SilcClientCommandContext cmd = (SilcClientCommandContext)context;
-  SilcClient client = cmd->client;
+  SilcClientCommandContext cmd = fsm_context;
   SilcClientConnection conn = cmd->conn;
+  SilcClient client = conn->client;
   SilcChannelEntry channel;
-  SilcBuffer buffer, idp, idp2;
+  SilcBuffer idp, idp2;
   SilcClientEntry target;
+  SilcDList clients;
   char *name;
   char *nickname = NULL;
 
-  if (!cmd->conn) {
-    SILC_NOT_CONNECTED(cmd->client, cmd->conn);
-    COMMAND_ERROR(SILC_STATUS_ERR_NOT_REGISTERED);
-    goto out;
-  }
-
   if (cmd->argc < 3) {
-    SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_INFO,
+    SAY(conn->client, conn, SILC_CLIENT_MESSAGE_INFO,
        "Usage: /KICK <channel> <nickname> [<comment>]");
     COMMAND_ERROR(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
     goto out;
@@ -1997,7 +2040,7 @@ SILC_CLIENT_CMD_FUNC(kick)
   }
 
   /* Get the Channel ID of the channel */
-  channel = silc_client_get_channel(cmd->client, conn, name);
+  channel = silc_client_get_channel(conn->client, conn, name);
   if (!channel) {
     COMMAND_ERROR(SILC_STATUS_ERR_NOT_ON_CHANNEL);
     goto out;
@@ -2010,209 +2053,187 @@ SILC_CLIENT_CMD_FUNC(kick)
     nickname = strdup(cmd->argv[2]);
 
   /* Get the target client */
-  target = silc_idlist_get_client(cmd->client, conn, nickname,
-                                 cmd->argv[2], FALSE);
-  if (!target) {
-    SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_INFO,
+  clients = silc_client_get_clients_local(client, conn, nickname,
+                                         cmd->argv[2]);
+  if (!clients) {
+    SAY(conn->client, conn, SILC_CLIENT_MESSAGE_INFO,
        "No such client: %s", cmd->argv[2]);
     COMMAND_ERROR(SILC_STATUS_ERR_NO_SUCH_NICK);
     goto out;
   }
+  target = silc_dlist_get(clients);
+  silc_dlist_uninit(clients);
 
   /* Send KICK command to the server */
   idp = silc_id_payload_encode(channel->id, SILC_ID_CHANNEL);
-  idp2 = silc_id_payload_encode(target->id, SILC_ID_CLIENT);
+  idp2 = silc_id_payload_encode(&target->id, SILC_ID_CLIENT);
   if (cmd->argc == 3)
-    buffer = silc_command_payload_encode_va(SILC_COMMAND_KICK,
-                                           ++conn->cmd_ident, 2,
-                                           1, idp->data, idp->len,
-                                           2, idp2->data, idp2->len);
+    silc_client_command_send_va(conn, cmd, cmd->cmd, NULL, NULL, 2,
+                               1, silc_buffer_datalen(idp),
+                               2, silc_buffer_datalen(idp2));
   else
-    buffer = silc_command_payload_encode_va(SILC_COMMAND_KICK,
-                                           ++conn->cmd_ident, 3,
-                                           1, idp->data, idp->len,
-                                           2, idp2->data, idp2->len,
-                                           3, cmd->argv[3],
-                                           strlen(cmd->argv[3]));
-  silc_client_packet_send(cmd->client, conn->sock, SILC_PACKET_COMMAND, NULL,
-                         0, NULL, NULL, buffer->data, buffer->len, TRUE);
-  silc_buffer_free(buffer);
+    silc_client_command_send_va(conn, cmd, cmd->cmd, NULL, NULL, 3,
+                               1, silc_buffer_datalen(idp),
+                               2, silc_buffer_datalen(idp2),
+                               3, cmd->argv[3], strlen(cmd->argv[3]));
+
   silc_buffer_free(idp);
   silc_buffer_free(idp2);
+  silc_free(nickname);
 
   /* Notify application */
   COMMAND(SILC_STATUS_OK);
 
+  /** Wait for command reply */
+  silc_fsm_next(fsm, silc_client_command_reply_wait);
+  return SILC_FSM_CONTINUE;
+
  out:
   silc_free(nickname);
-  silc_client_command_free(cmd);
+  return SILC_FSM_FINISH;
+}
+
+/***************************** OPER & SILCOPER ******************************/
+
+typedef struct {
+  unsigned char *passphrase;
+  SilcUInt32 passphrase_len;
+} *SilcClientCommandOper;
+
+/* Ask passphrase callback */
+
+static void silc_client_command_oper_cb(unsigned char *data,
+                                       SilcUInt32 data_len, void *context)
+{
+  SilcClientCommandContext cmd = context;
+  SilcClientCommandOper oper = cmd->context;
+
+  if (data && data_len)
+    oper->passphrase = silc_memdup(data, data_len);
+  oper->passphrase_len = data_len;
+
+  /* Continue */
+  SILC_FSM_CALL_CONTINUE(&cmd->thread);
 }
 
-static void silc_client_command_oper_send(unsigned char *data,
-                                         SilcUInt32 data_len, void *context)
+/* Send OPER/SILCOPER command */
+
+SILC_FSM_STATE(silc_client_command_oper_send)
 {
-  SilcClientCommandContext cmd = (SilcClientCommandContext)context;
+  SilcClientCommandContext cmd = fsm_context;
   SilcClientConnection conn = cmd->conn;
-  SilcBuffer buffer, auth;
+  SilcClientCommandOper oper = cmd->context;
+  SilcBuffer auth;
 
-  if (cmd->argc >= 3) {
+  if (!oper || !oper->passphrase) {
     /* Encode the public key authentication payload */
-    auth = silc_auth_public_key_auth_generate(cmd->client->public_key,
-                                             cmd->client->private_key,
-                                             cmd->client->rng,
+    auth = silc_auth_public_key_auth_generate(conn->public_key,
+                                             conn->private_key,
+                                             conn->client->rng,
                                              conn->internal->hash,
                                              conn->local_id,
                                              SILC_ID_CLIENT);
   } else {
     /* Encode the password authentication payload */
     auth = silc_auth_payload_encode(SILC_AUTH_PASSWORD, NULL, 0,
-                                   data, data_len);
+                                   oper->passphrase, oper->passphrase_len);
   }
 
-  buffer = silc_command_payload_encode_va(SILC_COMMAND_OPER,
-                                         ++conn->cmd_ident, 2,
-                                         1, cmd->argv[1],
-                                         strlen(cmd->argv[1]),
-                                         2, auth ? auth->data : NULL,
-                                         auth ? auth->len : 0);
-  silc_client_packet_send(cmd->client, conn->sock, SILC_PACKET_COMMAND, NULL,
-                         0, NULL, NULL, buffer->data, buffer->len, TRUE);
+  silc_client_command_send_va(conn, cmd, cmd->cmd, NULL, NULL, 2,
+                             1, cmd->argv[1], strlen(cmd->argv[1]),
+                             2, silc_buffer_datalen(auth));
 
-  silc_buffer_free(buffer);
   silc_buffer_clear(auth);
   silc_buffer_free(auth);
+  if (oper) {
+    silc_free(oper->passphrase);
+    silc_free(oper);
+  }
 
   /* Notify application */
   COMMAND(SILC_STATUS_OK);
+
+  /** Wait for command reply */
+  silc_fsm_next(fsm, silc_client_command_reply_wait);
+  return SILC_FSM_CONTINUE;
 }
 
 /* OPER command. Used to obtain server operator privileges. */
 
-SILC_CLIENT_CMD_FUNC(oper)
+SILC_FSM_STATE(silc_client_command_oper)
 {
-  SilcClientCommandContext cmd = (SilcClientCommandContext)context;
+  SilcClientCommandContext cmd = fsm_context;
   SilcClientConnection conn = cmd->conn;
-
-  if (!cmd->conn) {
-    SILC_NOT_CONNECTED(cmd->client, cmd->conn);
-    COMMAND_ERROR(SILC_STATUS_ERR_NOT_REGISTERED);
-    goto out;
-  }
+  SilcClientCommandOper oper;
 
   if (cmd->argc < 2) {
-    SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_INFO,
+    SAY(conn->client, conn, SILC_CLIENT_MESSAGE_INFO,
        "Usage: /OPER <username> [-pubkey]");
     COMMAND_ERROR(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
-    goto out;
+    return SILC_FSM_FINISH;
   }
 
+  /* Get passphrase */
   if (cmd->argc < 3) {
-    /* Get passphrase */
-    cmd->client->internal->ops->ask_passphrase(cmd->client, conn,
-                                    silc_client_command_oper_send,
-                                    context);
-    return;
+    oper = silc_calloc(1, sizeof(*oper));
+    if (!oper)
+      return SILC_FSM_FINISH;
+    cmd->context = oper;
+    SILC_FSM_CALL(conn->client->internal->
+                 ops->ask_passphrase(conn->client, conn,
+                                     silc_client_command_oper_cb, cmd));
   }
 
-  silc_client_command_oper_send(NULL, 0, context);
-
- out:
-  silc_client_command_free(cmd);
-}
-
-static void silc_client_command_silcoper_send(unsigned char *data,
-                                             SilcUInt32 data_len,
-                                             void *context)
-{
-  SilcClientCommandContext cmd = (SilcClientCommandContext)context;
-  SilcClientConnection conn = cmd->conn;
-  SilcBuffer buffer, auth;
-
-  if (cmd->argc >= 3) {
-    /* Encode the public key authentication payload */
-    auth = silc_auth_public_key_auth_generate(cmd->client->public_key,
-                                             cmd->client->private_key,
-                                             cmd->client->rng,
-                                             conn->internal->hash,
-                                             conn->local_id,
-                                             SILC_ID_CLIENT);
-  } else {
-    /* Encode the password authentication payload */
-    auth = silc_auth_payload_encode(SILC_AUTH_PASSWORD, NULL, 0,
-                                   data, data_len);
-  }
-
-  buffer = silc_command_payload_encode_va(SILC_COMMAND_SILCOPER,
-                                         ++conn->cmd_ident, 2,
-                                         1, cmd->argv[1],
-                                         strlen(cmd->argv[1]),
-                                         2, auth ? auth->data : NULL,
-                                         auth ? auth->len : 0);
-  silc_client_packet_send(cmd->client, conn->sock, SILC_PACKET_COMMAND, NULL,
-                         0, NULL, NULL, buffer->data, buffer->len, TRUE);
-
-  silc_buffer_free(buffer);
-  silc_buffer_clear(auth);
-  silc_buffer_free(auth);
-
-  /* Notify application */
-  COMMAND(SILC_STATUS_OK);
+  silc_fsm_next(fsm, silc_client_command_oper_send);
+  return SILC_FSM_CONTINUE;
 }
 
 /* SILCOPER command. Used to obtain router operator privileges. */
 
-SILC_CLIENT_CMD_FUNC(silcoper)
+SILC_FSM_STATE(silc_client_command_silcoper)
 {
-  SilcClientCommandContext cmd = (SilcClientCommandContext)context;
+  SilcClientCommandContext cmd = fsm_context;
   SilcClientConnection conn = cmd->conn;
-
-  if (!cmd->conn) {
-    SILC_NOT_CONNECTED(cmd->client, cmd->conn);
-    COMMAND_ERROR(SILC_STATUS_ERR_NOT_REGISTERED);
-    goto out;
-  }
+  SilcClientCommandOper oper;
 
   if (cmd->argc < 2) {
-    SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_INFO,
+    SAY(conn->client, conn, SILC_CLIENT_MESSAGE_INFO,
        "Usage: /SILCOPER <username> [-pubkey]");
     COMMAND_ERROR(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
-    goto out;
+    return SILC_FSM_FINISH;
   }
 
+  /* Get passphrase */
   if (cmd->argc < 3) {
-    /* Get passphrase */
-    cmd->client->internal->ops->ask_passphrase(cmd->client, conn,
-                                    silc_client_command_silcoper_send,
-                                    context);
-    return;
+    oper = silc_calloc(1, sizeof(*oper));
+    if (!oper)
+      return SILC_FSM_FINISH;
+    cmd->context = oper;
+    SILC_FSM_CALL(conn->client->internal->
+                 ops->ask_passphrase(conn->client, conn,
+                                     silc_client_command_oper_cb, cmd));
   }
 
-  silc_client_command_silcoper_send(NULL, 0, context);
-
- out:
-  silc_client_command_free(cmd);
+  silc_fsm_next(fsm, silc_client_command_oper_send);
+  return SILC_FSM_CONTINUE;
 }
 
+/*********************************** BAN ************************************/
+
 /* Command BAN. This is used to manage the ban list of the channel. */
 
-SILC_CLIENT_CMD_FUNC(ban)
+SILC_FSM_STATE(silc_client_command_ban)
 {
-  SilcClientCommandContext cmd = (SilcClientCommandContext)context;
+  SilcClientCommandContext cmd = fsm_context;
   SilcClientConnection conn = cmd->conn;
   SilcChannelEntry channel;
-  SilcBuffer buffer, chidp, args = NULL;
+  SilcBuffer chidp, args = NULL;
   char *name, *ban = NULL;
   unsigned char action[1];
   SilcPublicKey pubkey = NULL;
 
-  if (!cmd->conn) {
-    SILC_NOT_CONNECTED(cmd->client, cmd->conn);
-    COMMAND_ERROR(SILC_STATUS_ERR_NOT_REGISTERED);
-    goto out;
-  }
-
   if (cmd->argc < 2) {
-    SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_INFO,
+    SAY(conn->client, conn, SILC_CLIENT_MESSAGE_INFO,
        "Usage: /BAN <channel> "
        "[+|-[<nickname>[@<server>[!<username>[@hostname>]]]]]");
     COMMAND_ERROR(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
@@ -2229,7 +2250,7 @@ SILC_CLIENT_CMD_FUNC(ban)
   } else {
     name = cmd->argv[1];
 
-    channel = silc_client_get_channel(cmd->client, conn, name);
+    channel = silc_client_get_channel(conn->client, conn, name);
     if (!channel) {
       COMMAND_ERROR(SILC_STATUS_ERR_NOT_ON_CHANNEL);
       goto out;
@@ -2243,10 +2264,7 @@ SILC_CLIENT_CMD_FUNC(ban)
       action[0] = 0x01;
 
     /* Check if it is public key file to be added to invite list */
-    if (!silc_pkcs_load_public_key(cmd->argv[2] + 1, &pubkey,
-                                  SILC_PKCS_FILE_PEM))
-      silc_pkcs_load_public_key(cmd->argv[2] + 1, &pubkey,
-                               SILC_PKCS_FILE_BIN);
+    silc_pkcs_load_public_key(cmd->argv[2] + 1, &pubkey);
     ban = cmd->argv[2];
     if (!pubkey)
       ban++;
@@ -2258,9 +2276,9 @@ SILC_CLIENT_CMD_FUNC(ban)
                       SILC_STR_UI_SHORT(1),
                       SILC_STR_END);
     if (pubkey) {
-      chidp = silc_pkcs_public_key_payload_encode(pubkey);
-      args = silc_argument_payload_encode_one(args, chidp->data,
-                                             chidp->len, 2);
+      chidp = silc_public_key_payload_encode(pubkey);
+      args = silc_argument_payload_encode_one(args,
+                                             silc_buffer_datalen(chidp), 2);
       silc_buffer_free(chidp);
       silc_pkcs_public_key_free(pubkey);
     } else {
@@ -2271,77 +2289,62 @@ SILC_CLIENT_CMD_FUNC(ban)
   chidp = silc_id_payload_encode(channel->id, SILC_ID_CHANNEL);
 
   /* Send the command */
-  buffer = silc_command_payload_encode_va(SILC_COMMAND_BAN,
-                                         ++conn->cmd_ident, 3,
-                                         1, chidp->data, chidp->len,
-                                         2, args ? action : NULL,
-                                         args ? 1 : 0,
-                                         3, args ? args->data : NULL,
-                                         args ? args->len : 0);
-  silc_client_packet_send(cmd->client, conn->sock, SILC_PACKET_COMMAND, NULL,
-                         0, NULL, NULL, buffer->data, buffer->len, TRUE);
-  silc_buffer_free(buffer);
+  silc_client_command_send_va(conn, cmd, cmd->cmd, NULL, NULL, 3,
+                             1, silc_buffer_datalen(chidp),
+                             2, args ? action : NULL, args ? 1 : 0,
+                             3, silc_buffer_datalen(args));
+
   silc_buffer_free(chidp);
   silc_buffer_free(args);
 
   /* Notify application */
   COMMAND(SILC_STATUS_OK);
 
+  /** Wait for command reply */
+  silc_fsm_next(fsm, silc_client_command_reply_wait);
+  return SILC_FSM_CONTINUE;
+
  out:
-  silc_client_command_free(cmd);
+  return SILC_FSM_FINISH;
 }
 
+/********************************* DETACH ***********************************/
+
 /* Command DETACH. This is used to detach from the server */
 
-SILC_CLIENT_CMD_FUNC(detach)
+SILC_FSM_STATE(silc_client_command_detach)
 {
-  SilcClientCommandContext cmd = (SilcClientCommandContext)context;
+  SilcClientCommandContext cmd = fsm_context;
   SilcClientConnection conn = cmd->conn;
-  SilcBuffer buffer;
 
-  if (!cmd->conn) {
-    SILC_NOT_CONNECTED(cmd->client, cmd->conn);
-    COMMAND_ERROR(SILC_STATUS_ERR_NOT_REGISTERED);
-    goto out;
-  }
-
-  buffer = silc_command_payload_encode_va(SILC_COMMAND_DETACH,
-                                         ++conn->cmd_ident, 0);
-  silc_client_packet_send(cmd->client, conn->sock, SILC_PACKET_COMMAND, NULL,
-                         0, NULL, NULL, buffer->data, buffer->len, TRUE);
-  silc_buffer_free(buffer);
+  silc_client_command_send_va(conn, cmd, cmd->cmd, NULL, NULL, 0);
 
   /* Notify application */
   COMMAND(SILC_STATUS_OK);
 
- out:
-  silc_client_command_free(cmd);
+  /** Wait for command reply */
+  silc_fsm_next(fsm, silc_client_command_reply_wait);
+  return SILC_FSM_CONTINUE;
 }
 
+/********************************** WATCH ***********************************/
+
 /* Command WATCH. */
 
-SILC_CLIENT_CMD_FUNC(watch)
+SILC_FSM_STATE(silc_client_command_watch)
 {
-  SilcClientCommandContext cmd = (SilcClientCommandContext)context;
+  SilcClientCommandContext cmd = fsm_context;
   SilcClientConnection conn = cmd->conn;
-  SilcBuffer buffer, idp = NULL, args = NULL;
+  SilcBuffer args = NULL;
   int type = 0;
   const char *pubkey = NULL;
   SilcBool pubkey_add = TRUE;
 
-  if (!cmd->conn) {
-    SILC_NOT_CONNECTED(cmd->client, cmd->conn);
-    COMMAND_ERROR(SILC_STATUS_ERR_NOT_REGISTERED);
-    goto out;
-  }
-
   if (cmd->argc < 3) {
     COMMAND_ERROR(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
     goto out;
   }
 
-  idp = silc_id_payload_encode(conn->local_id, SILC_ID_CLIENT);
-
   if (!strcasecmp(cmd->argv[1], "-add")) {
     type = 2;
   } else if (!strcasecmp(cmd->argv[1], "-del")) {
@@ -2358,67 +2361,60 @@ SILC_CLIENT_CMD_FUNC(watch)
 
   if (pubkey) {
     SilcPublicKey pk;
+    SilcBuffer buffer;
 
-    if (!silc_pkcs_load_public_key(pubkey, &pk, SILC_PKCS_FILE_PEM)) {
-      if (!silc_pkcs_load_public_key(pubkey, &pk, SILC_PKCS_FILE_BIN)) {
-       SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_ERROR,
-           "Could not load public key %s, check the filename",
-           pubkey);
-       COMMAND_ERROR(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
-       goto out;
-      }
+    if (!silc_pkcs_load_public_key(pubkey, &pk)) {
+      SAY(conn->client, conn, SILC_CLIENT_MESSAGE_ERROR,
+         "Could not load public key %s, check the filename", pubkey);
+      COMMAND_ERROR(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
+      goto out;
     }
 
     args = silc_buffer_alloc_size(2);
     silc_buffer_format(args,
                       SILC_STR_UI_SHORT(1),
                       SILC_STR_END);
-    buffer = silc_pkcs_public_key_payload_encode(pk);
-    args = silc_argument_payload_encode_one(args, buffer->data, buffer->len,
+    buffer = silc_public_key_payload_encode(pk);
+    args = silc_argument_payload_encode_one(args, silc_buffer_datalen(buffer),
                                            pubkey_add ? 0x00 : 0x01);
     silc_buffer_free(buffer);
     silc_pkcs_public_key_free(pk);
   }
 
-  buffer = silc_command_payload_encode_va(SILC_COMMAND_WATCH,
-                                         ++conn->cmd_ident, 2,
-                                         1, idp->data, idp->len,
-                                         type,
-                                         pubkey ? args->data : cmd->argv[2],
-                                         pubkey ? args->len :
-                                         cmd->argv_lens[2]);
-  silc_client_packet_send(cmd->client, conn->sock, SILC_PACKET_COMMAND, NULL,
-                         0, NULL, NULL, buffer->data, buffer->len, TRUE);
-  silc_buffer_free(buffer);
+  /* Send the commmand */
+  silc_client_command_send_va(conn, cmd, cmd->cmd, NULL, NULL, 2,
+                             1, silc_buffer_datalen(conn->local_idp),
+                             type, pubkey ? args->data : cmd->argv[2],
+                             pubkey ? silc_buffer_len(args) :
+                             cmd->argv_lens[2]);
+
   silc_buffer_free(args);
 
   /* Notify application */
   COMMAND(SILC_STATUS_OK);
 
+  /** Wait for command reply */
+  silc_fsm_next(fsm, silc_client_command_reply_wait);
+  return SILC_FSM_CONTINUE;
+
  out:
-  if (idp)
-    silc_buffer_free(idp);
-  silc_client_command_free(cmd);
+  return SILC_FSM_FINISH;
 }
 
+/********************************** LEAVE ***********************************/
+
 /* LEAVE command. Leaves a channel. Client removes itself from a channel. */
 
-SILC_CLIENT_CMD_FUNC(leave)
+SILC_FSM_STATE(silc_client_command_leave)
 {
-  SilcClientCommandContext cmd = (SilcClientCommandContext)context;
+  SilcClientCommandContext cmd = fsm_context;
   SilcClientConnection conn = cmd->conn;
   SilcChannelEntry channel;
-  SilcBuffer buffer, idp;
+  SilcBuffer idp;
   char *name;
 
-  if (!cmd->conn) {
-    SILC_NOT_CONNECTED(cmd->client, cmd->conn);
-    COMMAND_ERROR(SILC_STATUS_ERR_NOT_REGISTERED);
-    goto out;
-  }
-
   if (cmd->argc != 2) {
-    SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_INFO,
+    SAY(conn->client, conn, SILC_CLIENT_MESSAGE_INFO,
        "Usage: /LEAVE <channel>");
     COMMAND_ERROR(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
     goto out;
@@ -2435,20 +2431,18 @@ SILC_CLIENT_CMD_FUNC(leave)
   }
 
   /* Get the channel entry */
-  channel = silc_client_get_channel(cmd->client, conn, name);
+  channel = silc_client_get_channel(conn->client, conn, name);
   if (!channel) {
     COMMAND_ERROR(SILC_STATUS_ERR_NOT_ON_CHANNEL);
     goto out;
   }
 
-  /* Send LEAVE command to the server */
   idp = silc_id_payload_encode(channel->id, SILC_ID_CHANNEL);
-  buffer = silc_command_payload_encode_va(SILC_COMMAND_LEAVE,
-                                         ++conn->cmd_ident, 1,
-                                         1, idp->data, idp->len);
-  silc_client_packet_send(cmd->client, conn->sock, SILC_PACKET_COMMAND, NULL,
-                         0, NULL, NULL, buffer->data, buffer->len, TRUE);
-  silc_buffer_free(buffer);
+
+  /* Send LEAVE command to the server */
+  silc_client_command_send_va(conn, cmd, cmd->cmd, NULL, NULL, 1,
+                             1, silc_buffer_datalen(idp));
+
   silc_buffer_free(idp);
 
   /* Notify application */
@@ -2457,28 +2451,27 @@ SILC_CLIENT_CMD_FUNC(leave)
   if (conn->current_channel == channel)
     conn->current_channel = NULL;
 
+  /** Wait for command reply */
+  silc_fsm_next(fsm, silc_client_command_reply_wait);
+  return SILC_FSM_CONTINUE;
+
  out:
-  silc_client_command_free(cmd);
+  return SILC_FSM_FINISH;
 }
 
+/********************************** USERS ***********************************/
+
 /* Command USERS. Requests the USERS of the clients joined on requested
    channel. */
 
-SILC_CLIENT_CMD_FUNC(users)
+SILC_FSM_STATE(silc_client_command_users)
 {
-  SilcClientCommandContext cmd = (SilcClientCommandContext)context;
+  SilcClientCommandContext cmd = fsm_context;
   SilcClientConnection conn = cmd->conn;
-  SilcBuffer buffer;
   char *name;
 
-  if (!cmd->conn) {
-    SILC_NOT_CONNECTED(cmd->client, cmd->conn);
-    COMMAND_ERROR(SILC_STATUS_ERR_NOT_REGISTERED);
-    goto out;
-  }
-
   if (cmd->argc != 2) {
-    SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_INFO,
+    SAY(conn->client, conn, SILC_CLIENT_MESSAGE_INFO,
        "Usage: /USERS <channel>");
     COMMAND_ERROR(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
     goto out;
@@ -2495,46 +2488,40 @@ SILC_CLIENT_CMD_FUNC(users)
   }
 
   /* Send USERS command to the server */
-  buffer = silc_command_payload_encode_va(SILC_COMMAND_USERS,
-                                         ++conn->cmd_ident, 1,
-                                         2, name, strlen(name));
-  silc_client_packet_send(cmd->client, conn->sock, SILC_PACKET_COMMAND,
-                         NULL, 0, NULL, NULL, buffer->data,
-                         buffer->len, TRUE);
-  silc_buffer_free(buffer);
+  silc_client_command_send_va(conn, cmd, cmd->cmd, NULL, NULL, 1,
+                             2, name, strlen(name));
 
   /* Notify application */
   COMMAND(SILC_STATUS_OK);
 
+  /** Wait for command reply */
+  silc_fsm_next(fsm, silc_client_command_reply_wait);
+  return SILC_FSM_CONTINUE;
+
  out:
-  silc_client_command_free(cmd);
+  return SILC_FSM_FINISH;
 }
 
+/********************************* GETKEY ***********************************/
+
 /* Command GETKEY. Used to fetch remote client's public key. */
 
-SILC_CLIENT_CMD_FUNC(getkey)
+SILC_FSM_STATE(silc_client_command_getkey)
 {
-  SilcClientCommandContext cmd = (SilcClientCommandContext)context;
+  SilcClientCommandContext cmd = fsm_context;
   SilcClientConnection conn = cmd->conn;
-  SilcClient client = cmd->client;
-  SilcClientEntry client_entry = NULL;
-  SilcServerEntry server_entry = NULL;
+  SilcClient client = conn->client;
+  SilcClientEntry client_entry;
+  SilcServerEntry server_entry;
+  SilcDList clients;
   char *nickname = NULL;
-  SilcBuffer idp, buffer;
-
-  SILC_LOG_DEBUG(("Start"));
-
-  if (!cmd->conn) {
-    SILC_NOT_CONNECTED(cmd->client, cmd->conn);
-    COMMAND_ERROR(SILC_STATUS_ERR_NOT_REGISTERED);
-    goto out;
-  }
+  SilcBuffer idp;
 
   if (cmd->argc < 2) {
     client->internal->ops->say(client, conn, SILC_CLIENT_MESSAGE_INFO,
                     "Usage: /GETKEY <nickname or server name>");
     COMMAND_ERROR(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
-    goto out;
+    return SILC_FSM_FINISH;
   }
 
   /* Parse the typed nickname. */
@@ -2542,109 +2529,66 @@ SILC_CLIENT_CMD_FUNC(getkey)
     client->internal->params->nickname_parse(cmd->argv[1], &nickname);
   else
     nickname = strdup(cmd->argv[1]);
+  if (!nickname)
+    return SILC_FSM_FINISH;
 
   /* Find client entry */
-  client_entry = silc_idlist_get_client(client, conn, nickname, cmd->argv[1],
-                                       FALSE);
-  if (!client_entry) {
-    /* Check whether user requested server actually */
+  clients = silc_client_get_clients_local(client, conn, nickname,
+                                         cmd->argv[1]);
+  if (!clients) {
+    /* Check whether user requested server */
     server_entry = silc_client_get_server(client, conn, cmd->argv[1]);
-
     if (!server_entry) {
-      /* No. what ever user wants we don't have it, so resolve it. We
-        will first try to resolve the client, and if that fails then
-        we'll try to resolve the server. */
-
-      if (!cmd->pending) {
-       /* This will send the IDENTIFY command for nickname */
-       silc_idlist_get_client(client, conn, nickname, cmd->argv[1], TRUE);
-       silc_client_command_pending(conn, SILC_COMMAND_IDENTIFY,
-                                   conn->cmd_ident,
-                                   silc_client_command_getkey,
-                                   silc_client_command_dup(cmd));
-       cmd->pending = 1;
-       goto out;
-      } else {
-       SilcClientCommandReplyContext reply =
-         (SilcClientCommandReplyContext)context2;
-       SilcStatus error;
-
-       /* If nickname was not found, then resolve the server. */
-       silc_command_get_status(reply->payload, NULL, &error);
-       if (error == SILC_STATUS_ERR_NO_SUCH_NICK) {
-         /* This sends the IDENTIFY command to resolve the server. */
-         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,
-                                     silc_client_command_getkey,
-                                     silc_client_command_dup(cmd));
-         goto out;
-       }
-
-       /* If server was not found, then we've resolved both nickname and
-          server and did not find anybody. */
-       if (error == SILC_STATUS_ERR_NO_SUCH_SERVER) {
-         SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_ERROR, "%s",
-            silc_get_status_message(SILC_STATUS_ERR_NO_SUCH_NICK));
-         SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_ERROR, "%s",
-           silc_get_status_message(error));
-         COMMAND_ERROR(SILC_STATUS_ERR_NO_SUCH_NICK);
-         goto out;
-       }
-
-       COMMAND_ERROR(error);
-       goto out;
-      }
+      /* No client or server exist with this name, query for both. */
+      SILC_FSM_CALL(silc_client_command_send(client, conn,
+                                            SILC_COMMAND_IDENTIFY,
+                                            silc_client_command_continue,
+                                            cmd, 2,
+                                            1, cmd->argv[1],
+                                            strlen(cmd->argv[1]),
+                                            2, cmd->argv[1],
+                                            strlen(cmd->argv[1])));
     }
-
     idp = silc_id_payload_encode(server_entry->server_id, SILC_ID_SERVER);
   } else {
-    idp = silc_id_payload_encode(client_entry->id, SILC_ID_CLIENT);
+    client_entry = silc_dlist_get(clients);
+    idp = silc_id_payload_encode(&client_entry->id, SILC_ID_CLIENT);
+    silc_dlist_uninit(clients);
   }
 
-  buffer = silc_command_payload_encode_va(SILC_COMMAND_GETKEY,
-                                         ++conn->cmd_ident, 1,
-                                         1, idp->data, idp->len);
-  silc_client_packet_send(cmd->client, conn->sock, SILC_PACKET_COMMAND, NULL,
-                         0, NULL, NULL, buffer->data, buffer->len, TRUE);
-  silc_buffer_free(buffer);
+  /* Send the commmand */
+  silc_client_command_send_va(conn, cmd, cmd->cmd, NULL, NULL, 1,
+                             1, silc_buffer_datalen(idp));
+
   silc_buffer_free(idp);
+  silc_free(nickname);
 
   /* Notify application */
   COMMAND(SILC_STATUS_OK);
 
- out:
-  silc_free(nickname);
-  silc_client_command_free(cmd);
+  /** Wait for command reply */
+  silc_fsm_next(fsm, silc_client_command_reply_wait);
+  return SILC_FSM_CONTINUE;
 }
 
+/********************************* SERVICE **********************************/
+
 /* Command SERVICE.  Negotiates service agreement with server. */
 /* XXX incomplete */
 
-SILC_CLIENT_CMD_FUNC(service)
+SILC_FSM_STATE(silc_client_command_service)
 {
-  SilcClientCommandContext cmd = (SilcClientCommandContext)context;
+  SilcClientCommandContext cmd = fsm_context;
+#if 0
   SilcClientConnection conn = cmd->conn;
   SilcBuffer buffer;
   char *name;
 
-  if (!cmd->conn) {
-    SILC_NOT_CONNECTED(cmd->client, cmd->conn);
-    COMMAND_ERROR(SILC_STATUS_ERR_NOT_REGISTERED);
-    goto out;
-  }
-
   if (cmd->argc < 2) {
-    SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_INFO,
+    SAY(conn->client, conn, SILC_CLIENT_MESSAGE_INFO,
        "Usage: /SERVICE [<service name>] [-pubkey]");
     COMMAND_ERROR(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
-    goto out;
+    return SILC_FSM_FINISH;
   }
 
   name = cmd->argv[1];
@@ -2653,205 +2597,18 @@ SILC_CLIENT_CMD_FUNC(service)
   buffer = silc_command_payload_encode_va(SILC_COMMAND_SERVICE,
                                          ++conn->cmd_ident, 1,
                                          1, name, strlen(name));
-  silc_client_packet_send(cmd->client, conn->sock, SILC_PACKET_COMMAND,
+  silc_client_packet_send(conn->client, conn->sock, SILC_PACKET_COMMAND,
                          NULL, 0, NULL, NULL, buffer->data,
                          buffer->len, TRUE);
   silc_buffer_free(buffer);
+#endif /* 0 */
 
   /* Notify application */
   COMMAND(SILC_STATUS_OK);
 
- out:
-  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. */
-
-SilcBool silc_client_command_register(SilcClient client,
-                                 SilcCommand command,
-                                 const char *name,
-                                 SilcCommandCb command_function,
-                                 SilcCommandCb command_reply_function,
-                                 SilcUInt8 max_args,
-                                 SilcUInt16 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. */
-
-SilcBool silc_client_command_unregister(SilcClient client,
-                                   SilcCommand command,
-                                   SilcCommandCb command_function,
-                                   SilcCommandCb command_reply_function,
-                                   SilcUInt16 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;
-}
-
-/* Private range commands, specific to this implementation (and compatible
-   with SILC Server). */
-
-/* CONNECT command. Connects the server to another server. */
-
-SILC_CLIENT_CMD_FUNC(connect)
-{
-  SilcClientCommandContext cmd = (SilcClientCommandContext)context;
-  SilcClientConnection conn = cmd->conn;
-  SilcBuffer buffer;
-  unsigned char port[4];
-  SilcUInt32 tmp;
-
-  if (!cmd->conn) {
-    SILC_NOT_CONNECTED(cmd->client, cmd->conn);
-    COMMAND_ERROR(SILC_STATUS_ERR_NOT_REGISTERED);
-    goto out;
-  }
-
-  if (cmd->argc < 2) {
-    SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_INFO,
-       "Usage: /CONNECT <server> [<port>]");
-    COMMAND_ERROR(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
-    goto out;
-  }
-
-  if (cmd->argc == 3) {
-    tmp = atoi(cmd->argv[2]);
-    SILC_PUT32_MSB(tmp, port);
-  }
-
-  if (cmd->argc == 3)
-    buffer = silc_command_payload_encode_va(SILC_COMMAND_PRIV_CONNECT,
-                                           ++conn->cmd_ident, 2,
-                                           1, cmd->argv[1],
-                                           strlen(cmd->argv[1]),
-                                           2, port, 4);
-  else
-    buffer = silc_command_payload_encode_va(SILC_COMMAND_PRIV_CONNECT,
-                                           ++conn->cmd_ident, 1,
-                                           1, cmd->argv[1],
-                                           strlen(cmd->argv[1]));
-  silc_client_packet_send(cmd->client, conn->sock, SILC_PACKET_COMMAND, NULL,
-                         0, NULL, NULL, buffer->data, buffer->len, TRUE);
-  silc_buffer_free(buffer);
-
-  /* Notify application */
-  COMMAND(SILC_STATUS_OK);
-
- out:
-  silc_client_command_free(cmd);
-}
-
-
-/* CLOSE command. Close server connection to the remote server */
-
-SILC_CLIENT_CMD_FUNC(close)
-{
-  SilcClientCommandContext cmd = (SilcClientCommandContext)context;
-  SilcClientConnection conn = cmd->conn;
-  SilcBuffer buffer;
-  unsigned char port[4];
-  SilcUInt32 tmp;
-
-  if (!cmd->conn) {
-    SILC_NOT_CONNECTED(cmd->client, cmd->conn);
-    COMMAND_ERROR(SILC_STATUS_ERR_NOT_REGISTERED);
-    goto out;
-  }
-
-  if (cmd->argc < 2) {
-    SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_INFO,
-       "Usage: /CLOSE <server> [<port>]");
-    COMMAND_ERROR(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
-    goto out;
-  }
-
-  if (cmd->argc == 3) {
-    tmp = atoi(cmd->argv[2]);
-    SILC_PUT32_MSB(tmp, port);
-  }
-
-  if (cmd->argc == 3)
-    buffer = silc_command_payload_encode_va(SILC_COMMAND_PRIV_CLOSE,
-                                           ++conn->cmd_ident, 2,
-                                           1, cmd->argv[1],
-                                           strlen(cmd->argv[1]),
-                                           2, port, 4);
-  else
-    buffer = silc_command_payload_encode_va(SILC_COMMAND_PRIV_CLOSE,
-                                           ++conn->cmd_ident, 1,
-                                           1, cmd->argv[1],
-                                           strlen(cmd->argv[1]));
-  silc_client_packet_send(cmd->client, conn->sock, SILC_PACKET_COMMAND, NULL,
-                         0, NULL, NULL, buffer->data, buffer->len, TRUE);
-  silc_buffer_free(buffer);
-
-  /* Notify application */
-  COMMAND(SILC_STATUS_OK);
-
- out:
-  silc_client_command_free(cmd);
-}
-
-/* SHUTDOWN command. Shutdowns the server. */
-
-SILC_CLIENT_CMD_FUNC(shutdown)
-{
-  SilcClientCommandContext cmd = (SilcClientCommandContext)context;
-
-  if (!cmd->conn) {
-    SILC_NOT_CONNECTED(cmd->client, cmd->conn);
-    COMMAND_ERROR(SILC_STATUS_ERR_NOT_REGISTERED);
-    goto out;
-  }
-
-  /* Send the command */
-  silc_client_command_send(cmd->client, cmd->conn,
-                          SILC_COMMAND_PRIV_SHUTDOWN, 0, 0);
-
-  /* Notify application */
-  COMMAND(SILC_STATUS_OK);
-
- out:
-  silc_client_command_free(cmd);
+  /** Wait for command reply */
+  silc_fsm_next(fsm, silc_client_command_reply_wait);
+  return SILC_FSM_CONTINUE;
 }
 
 /* Register all default commands provided by the client library for the
@@ -2889,10 +2646,6 @@ void silc_client_commands_register(SilcClient client)
   SILC_CLIENT_CMD(users, USERS, "USERS", 2);
   SILC_CLIENT_CMD(getkey, GETKEY, "GETKEY", 2);
   SILC_CLIENT_CMD(service, SERVICE, "SERVICE", 10);
-
-  SILC_CLIENT_CMD(connect, PRIV_CONNECT, "CONNECT", 3);
-  SILC_CLIENT_CMD(close, PRIV_CLOSE, "CLOSE", 3);
-  SILC_CLIENT_CMD(shutdown, PRIV_SHUTDOWN, "SHUTDOWN", 1);
 }
 
 /* Unregister all commands. */
@@ -2926,41 +2679,79 @@ void silc_client_commands_unregister(SilcClient client)
   SILC_CLIENT_CMDU(users, USERS, "USERS");
   SILC_CLIENT_CMDU(getkey, GETKEY, "GETKEY");
   SILC_CLIENT_CMDU(service, SERVICE, "SERVICE");
-
-  SILC_CLIENT_CMDU(connect, PRIV_CONNECT, "CONNECT");
-  SILC_CLIENT_CMDU(close, PRIV_CLOSE, "CLOSE");
-  SILC_CLIENT_CMDU(shutdown, PRIV_SHUTDOWN, "SHUTDOWN");
 }
 
-/**** Client side incoming command handling **********************************/
+/****************** Client Side Incoming Command Handling *******************/
+
+/* Reply to WHOIS command from server */
+
+static void silc_client_command_process_whois(SilcClient client,
+                                             SilcClientConnection conn,
+                                             SilcCommandPayload payload,
+                                             SilcArgumentPayload args)
+{
+#if 0
+  SilcDList attrs;
+  unsigned char *tmp;
+  SilcUInt32 tmp_len;
+  SilcBuffer buffer, packet;
+
+  SILC_LOG_DEBUG(("Received WHOIS command"));
+
+  /* Try to take the Requested Attributes */
+  tmp = silc_argument_get_arg_type(args, 3, &tmp_len);
+  if (!tmp)
+    return;
+
+  attrs = silc_attribute_payload_parse(tmp, tmp_len);
+  if (!attrs)
+    return;
+
+  /* Process requested attributes */
+  buffer = silc_client_attributes_process(client, conn, attrs);
+  if (!buffer) {
+    silc_attribute_payload_list_free(attrs);
+    return;
+  }
 
-void silc_client_command_process_whois(SilcClient client,
-                                      SilcSocketConnection sock,
-                                      SilcCommandPayload payload,
-                                      SilcArgumentPayload args);
+  /* Send the attributes back */
+  packet =
+    silc_command_reply_payload_encode_va(SILC_COMMAND_WHOIS,
+                                        SILC_STATUS_OK, 0,
+                                        silc_command_get_ident(payload),
+                                        1, 11, buffer->data, buffer->len);
+  silc_client_packet_send(client, sock, SILC_PACKET_COMMAND_REPLY,
+                         NULL, 0, NULL, NULL, packet->data,
+                         packet->len, TRUE);
+  silc_buffer_free(packet);
+  silc_buffer_free(buffer);
+#endif /* 0 */
+}
 
 /* Client is able to receive some command packets even though they are
    special case.  Server may send WHOIS command to the client to retrieve
    Requested Attributes information for WHOIS query the server is
    processing. This function currently handles only the WHOIS command,
-   but if in the future for commands may arrive then this can be made
+   but if in the future more commands may arrive then this can be made
    to support other commands too. */
 
-void silc_client_command_process(SilcClient client,
-                                SilcSocketConnection sock,
-                                SilcPacketContext *packet)
+SILC_FSM_STATE(silc_client_command)
 {
+  SilcClientConnection conn = fsm_context;
+  SilcClient client = conn->client;
+  SilcPacket packet = state_context;
   SilcCommandPayload payload;
   SilcCommand command;
   SilcArgumentPayload args;
 
   /* Get command payload from packet */
-  payload = silc_command_payload_parse(packet->buffer->data,
-                                      packet->buffer->len);
+  payload = silc_command_payload_parse(packet->buffer.data,
+                                      silc_buffer_len(&packet->buffer));
   if (!payload) {
-    /* Silently ignore bad reply packet */
+    /** Bad command payload */
     SILC_LOG_DEBUG(("Bad command packet"));
-    return;
+    silc_fsm_next(fsm, silc_client_connection_st_packet);
+    return SILC_FSM_CONTINUE;
   }
 
   /* Get arguments */
@@ -2975,7 +2766,7 @@ void silc_client_command_process(SilcClient client,
     if (client->internal->params->ignore_requested_attributes)
       break;
 
-    silc_client_command_process_whois(client, sock, payload, args);
+    silc_client_command_process_whois(client, conn, payload, args);
     break;
 
   default:
@@ -2983,45 +2774,8 @@ void silc_client_command_process(SilcClient client,
   }
 
   silc_command_payload_free(payload);
-}
-
-void silc_client_command_process_whois(SilcClient client,
-                                      SilcSocketConnection sock,
-                                      SilcCommandPayload payload,
-                                      SilcArgumentPayload args)
-{
-  SilcDList attrs;
-  unsigned char *tmp;
-  SilcUInt32 tmp_len;
-  SilcBuffer buffer, packet;
-
-  SILC_LOG_DEBUG(("Received WHOIS command"));
-
-  /* Try to take the Requested Attributes */
-  tmp = silc_argument_get_arg_type(args, 3, &tmp_len);
-  if (!tmp)
-    return;
-
-  attrs = silc_attribute_payload_parse(tmp, tmp_len);
-  if (!attrs)
-    return;
 
-  /* Process requested attributes */
-  buffer = silc_client_attributes_process(client, sock, attrs);
-  if (!buffer) {
-    silc_attribute_payload_list_free(attrs);
-    return;
-  }
-
-  /* Send the attributes back */
-  packet =
-    silc_command_reply_payload_encode_va(SILC_COMMAND_WHOIS,
-                                        SILC_STATUS_OK, 0,
-                                        silc_command_get_ident(payload),
-                                        1, 11, buffer->data, buffer->len);
-  silc_client_packet_send(client, sock, SILC_PACKET_COMMAND_REPLY,
-                         NULL, 0, NULL, NULL, packet->data,
-                         packet->len, TRUE);
-  silc_buffer_free(packet);
-  silc_buffer_free(buffer);
+  /** Packet processed */
+  silc_fsm_next(fsm, silc_client_connection_st_packet);
+  return SILC_FSM_CONTINUE;
 }
index ca9c584ed3dc3609474a668df7978216a9fb5785..dfcb60c20bb0827ae74454a5adeee50acd37354d 100644 (file)
@@ -4,7 +4,7 @@
 
   Author: Pekka Riikonen <priikone@silcnet.org>
 
-  Copyright (C) 1997 - 2005 Pekka Riikonen
+  Copyright (C) 2006 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
 #ifndef COMMAND_H
 #define COMMAND_H
 
-#include "command_reply.h"
+SILC_FSM_STATE(silc_client_command);
+SILC_FSM_STATE(silc_client_command_whois);
+SILC_FSM_STATE(silc_client_command_whowas);
+SILC_FSM_STATE(silc_client_command_identify);
+SILC_FSM_STATE(silc_client_command_nick);
+SILC_FSM_STATE(silc_client_command_list);
+SILC_FSM_STATE(silc_client_command_topic);
+SILC_FSM_STATE(silc_client_command_invite);
+SILC_FSM_STATE(silc_client_command_quit);
+SILC_FSM_STATE(silc_client_command_kill);
+SILC_FSM_STATE(silc_client_command_info);
+SILC_FSM_STATE(silc_client_command_ping);
+SILC_FSM_STATE(silc_client_command_oper);
+SILC_FSM_STATE(silc_client_command_join);
+SILC_FSM_STATE(silc_client_command_motd);
+SILC_FSM_STATE(silc_client_command_umode);
+SILC_FSM_STATE(silc_client_command_cmode);
+SILC_FSM_STATE(silc_client_command_cumode);
+SILC_FSM_STATE(silc_client_command_kick);
+SILC_FSM_STATE(silc_client_command_ban);
+SILC_FSM_STATE(silc_client_command_detach);
+SILC_FSM_STATE(silc_client_command_watch);
+SILC_FSM_STATE(silc_client_command_silcoper);
+SILC_FSM_STATE(silc_client_command_leave);
+SILC_FSM_STATE(silc_client_command_users);
+SILC_FSM_STATE(silc_client_command_getkey);
+SILC_FSM_STATE(silc_client_command_service);
 
-/* 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.
-
-   To call a command: command->command(cmd, NULL);
-   To call a command reply: command->reply(cmd, NULL);
-
-*/
-struct SilcClientCommandStruct {
-  SilcCommand cmd;                /* Command type */
-  SilcCommandCb command;          /* Command function */
-  SilcCommandCb reply;            /* Command reply callback */
-  char *name;                     /* Name of the command (optional) */
-  SilcUInt8 max_args;             /* Maximum arguments (optional)  */
-  SilcUInt16 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. */
-struct SilcClientCommandContextStruct {
-  SilcClient client;
-  SilcClientConnection conn;
-  SilcClientCommand command;
-  SilcUInt32 argc;
-  unsigned char **argv;
-  SilcUInt32 *argv_lens;
-  SilcUInt32 *argv_types;
-  int pending;                 /* Command is being re-processed when TRUE */
-  int users;                   /* Reference counter */
-};
-
-/* Structure holding pending commands. If command is pending it will be
-   executed after command reply has been executed. */
-typedef struct SilcClientCommandPendingStruct {
-  SilcCommand reply_cmd;
-  SilcUInt16 ident;
-  unsigned int reply_check : 8;
-  SilcCommandCb callback;
-  void *context;
-  struct SilcClientCommandPendingStruct *next;
-} SilcClientCommandPending;
-
-/* List of pending commands */
-extern SilcClientCommandPending *silc_command_pending;
-
-
-/* Macros */
-
-/* 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)                             \
-void silc_client_command_##func(void *context, void *context2)
-
-/* Executed pending command callback */
-#define SILC_CLIENT_PENDING_EXEC(ctx, cmd)                               \
-do {                                                                     \
-  int _i;                                                                \
-  for (_i = 0; _i < ctx->callbacks_count; _i++)                                  \
-    if (ctx->callbacks[_i].callback)                                     \
-      (*ctx->callbacks[_i].callback)(ctx->callbacks[_i].context, ctx);   \
-  silc_client_command_pending_del(ctx->sock->user_data, cmd, ctx->ident); \
-} while(0)
-
-SilcClientCommandContext silc_client_command_alloc(void);
-void silc_client_command_free(SilcClientCommandContext ctx);
-SilcClientCommandContext silc_client_command_dup(SilcClientCommandContext ctx);
-SilcClientCommand silc_client_command_find(SilcClient client,
-                                          const char *name);
-SilcBool silc_client_command_register(SilcClient client,
-                                 SilcCommand command,
-                                 const char *name,
-                                 SilcCommandCb command_function,
-                                 SilcCommandCb command_reply_function,
-                                 SilcUInt8 max_args,
-                                 SilcUInt16 ident);
-SilcBool silc_client_command_unregister(SilcClient client,
-                                   SilcCommand command,
-                                   SilcCommandCb command_function,
-                                   SilcCommandCb command_reply_function,
-                                   SilcUInt16 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,
-                                    SilcUInt16 ident);
-SilcClientCommandPendingCallbacks
-silc_client_command_pending_check(SilcClientConnection conn,
-                                 SilcClientCommandReplyContext ctx,
-                                 SilcCommand command,
-                                 SilcUInt16 ident,
-                                 SilcUInt32 *callbacks_count);
-void silc_client_command_process(SilcClient client,
-                                SilcSocketConnection sock,
-                                SilcPacketContext *packet);
-SILC_CLIENT_CMD_FUNC(whois);
-SILC_CLIENT_CMD_FUNC(whowas);
-SILC_CLIENT_CMD_FUNC(identify);
-SILC_CLIENT_CMD_FUNC(nick);
-SILC_CLIENT_CMD_FUNC(list);
-SILC_CLIENT_CMD_FUNC(topic);
-SILC_CLIENT_CMD_FUNC(invite);
-SILC_CLIENT_CMD_FUNC(quit);
-SILC_CLIENT_CMD_FUNC(kill);
-SILC_CLIENT_CMD_FUNC(info);
-SILC_CLIENT_CMD_FUNC(ping);
-SILC_CLIENT_CMD_FUNC(oper);
-SILC_CLIENT_CMD_FUNC(join);
-SILC_CLIENT_CMD_FUNC(motd);
-SILC_CLIENT_CMD_FUNC(umode);
-SILC_CLIENT_CMD_FUNC(cmode);
-SILC_CLIENT_CMD_FUNC(cumode);
-SILC_CLIENT_CMD_FUNC(kick);
-SILC_CLIENT_CMD_FUNC(ban);
-SILC_CLIENT_CMD_FUNC(detach);
-SILC_CLIENT_CMD_FUNC(watch);
-SILC_CLIENT_CMD_FUNC(silcoper);
-SILC_CLIENT_CMD_FUNC(leave);
-SILC_CLIENT_CMD_FUNC(users);
-SILC_CLIENT_CMD_FUNC(getkey);
-SILC_CLIENT_CMD_FUNC(service);
-
-SILC_CLIENT_CMD_FUNC(shutdown);
-SILC_CLIENT_CMD_FUNC(close);
-SILC_CLIENT_CMD_FUNC(connect);
 
-#endif
+#endif /* COMMAND_H */
index 37384977c1e04cf8245669a25f5630a38baff8ba..0fd7990366b3d69fb79c74154dc7b6dee2438617 100644 (file)
@@ -4,7 +4,7 @@
 
   Author: Pekka Riikonen <priikone@silcnet.org>
 
-  Copyright (C) 1997 - 2005 Pekka Riikonen
+  Copyright (C) 1997 - 2006 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
 
 */
 /* $Id$ */
-/*
- * Command reply functions are "the otherside" of the command functions.
- * Reply to a command sent by server is handled by these functions.
- *
- * The arguments received from server are also passed to the calling
- * application through command_reply client operation.  The arguments are
- * exactly same and in same order as the server sent it.  However, ID's are
- * not sent to the application.  Instead, corresponding ID entry is sent
- * to the application.  For example, instead of sending Client ID the
- * corresponding SilcClientEntry is sent to the application.  The case is
- * same with for example Channel ID's.  This way application has all the
- * necessary data already in hand without redundant searching.  If ID is
- * received but ID entry does not exist, NULL is sent.
- */
 
 #include "silc.h"
 #include "silcclient.h"
 #include "client_internal.h"
 
-#define SAY cmd->client->internal->ops->say
-
-/* All functions that call the COMMAND_CHECK_STATUS macro must have
-   out: and err: goto labels. out label should call the pending
-   command replies, and the err label just handle error condition. */
+/************************** Types and definitions ***************************/
 
-#define COMMAND_CHECK_STATUS                                   \
+/* Calls error command reply callback back to command sender. */
+#define ERROR_CALLBACK(error)                                  \
 do {                                                           \
-  SILC_LOG_DEBUG(("Start"));                                   \
-  if (!silc_command_get_status(cmd->payload, NULL, NULL)) {    \
-    if (SILC_STATUS_IS_ERROR(cmd->status)) {                   \
-      /* Single error */                                       \
-      COMMAND_REPLY_ERROR(cmd->status);                                \
-      goto out;                                                        \
-    }                                                          \
-    /* List of errors */                                       \
-    COMMAND_REPLY_ERROR(cmd->error);                           \
-    if (cmd->status == SILC_STATUS_LIST_END)                   \
-      goto out;                                                        \
-    goto err;                                                  \
-  }                                                            \
+  void *arg1 = NULL, *arg2 = NULL;                             \
+  if (cmd->status != SILC_STATUS_OK)                           \
+    silc_status_get_args(cmd->status, args, &arg1, &arg2);     \
+  else                                                         \
+    cmd->status = error;                                       \
+   silc_client_command_callback(cmd, arg1, arg2);              \
 } while(0)
 
-/* Same as COMMAND_CHECK_STATUS but doesn't call client operation */
-#define COMMAND_CHECK_STATUS_I                                 \
-do {                                                           \
-  SILC_LOG_DEBUG(("Start"));                                   \
-  if (!silc_command_get_status(cmd->payload, NULL, NULL)) {    \
-    if (SILC_STATUS_IS_ERROR(cmd->status))                     \
-      goto out;                                                        \
-    if (cmd->status == SILC_STATUS_LIST_END)                   \
-      goto out;                                                        \
-    goto err;                                                  \
-  }                                                            \
-} while(0)
+/* Check for error */
+#define CHECK_STATUS(msg)                                              \
+  SILC_LOG_DEBUG(("Start"));                                           \
+  if (cmd->error != SILC_STATUS_OK) {                                  \
+    if (cmd->verbose)                                                  \
+      SAY(cmd->conn->client, cmd->conn, SILC_CLIENT_MESSAGE_ERROR,     \
+         msg "%s", silc_get_status_message(cmd->error));               \
+    ERROR_CALLBACK(cmd->error);                                                \
+    silc_client_command_process_error(cmd, state_context, cmd->error); \
+    silc_fsm_next(fsm, silc_client_command_reply_process);             \
+    return SILC_FSM_YIELD;                                             \
+  }
+
+/* Check for correct arguments */
+#define CHECK_ARGS(min, max)                                   \
+  if (silc_argument_get_arg_num(args) < min ||                 \
+      silc_argument_get_arg_num(args) > max) {                 \
+    ERROR_CALLBACK(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);         \
+    silc_fsm_next(fsm, silc_client_command_reply_process);     \
+    return SILC_FSM_YIELD;                                     \
+  }
+
+#define SAY cmd->conn->client->internal->ops->say
+
+/************************ Static utility functions **************************/
+
+/* Delivers the command reply back to application */
+
+static inline void
+silc_client_command_callback(SilcClientCommandContext cmd, ...)
+{
+  SilcClientCommandReplyCallback cb;
+  va_list ap, cp;
+
+  va_start(ap, cmd);
+
+  /* Default reply callback */
+  if (cmd->called) {
+    silc_va_copy(cp, ap);
+    cmd->conn->client->internal->ops->command_reply(
+                      cmd->conn->client, cmd->conn, cmd->cmd, cmd->status,
+                      cmd->error, cp);
+    va_end(cp);
+  }
+
+  /* Reply callback */
+  silc_list_start(cmd->reply_callbacks);
+  while ((cb = silc_list_get(cmd->reply_callbacks)))
+    if (!cb->do_not_call) {
+      silc_va_copy(cp, ap);
+      cb->do_not_call = cb->reply(cmd->conn->client, cmd->conn, cmd->cmd,
+                                 cmd->status, cmd->error, cb->context, cp);
+      va_end(cp);
+    }
+
+  va_end(ap);
+}
+
+/* Handles common error status types. */
 
-/* Process received command reply. */
+static void silc_client_command_process_error(SilcClientCommandContext cmd,
+                                             SilcCommandPayload payload,
+                                             SilcStatus error)
+{
+  SilcClient client = cmd->conn->client;
+  SilcClientConnection conn = cmd->conn;
+  SilcArgumentPayload args = silc_command_get_args(payload);
+  SilcClientEntry client_entry;
+  SilcID id;
+
+  if (cmd->error == SILC_STATUS_ERR_NO_SUCH_CLIENT_ID) {
+    /* Remove unknown client entry from cache */
+    if (!silc_argument_get_decoded(args, 2, SILC_ARGUMENT_ID, &id, NULL))
+      return;
+
+    client_entry = silc_client_get_client_by_id(client, conn, &id.u.client_id);
+    if (client_entry)
+      silc_client_del_client(client, conn, client_entry);
+  }
+}
 
-void silc_client_command_reply_process(SilcClient client,
-                                      SilcSocketConnection sock,
-                                      SilcPacketContext *packet)
+/***************************** Command Reply ********************************/
+
+/* Process received command reply packet */
+
+SILC_FSM_STATE(silc_client_command_reply)
 {
-  SilcBuffer buffer = packet->buffer;
-  SilcClientCommand cmd;
-  SilcClientCommandReplyContext ctx;
+  SilcClientConnection conn = fsm_context;
+  SilcPacket packet = state_context;
+  SilcClientCommandContext cmd;
   SilcCommandPayload payload;
   SilcCommand command;
-  SilcCommandCb reply = NULL;
+  SilcUInt16 cmd_ident;
 
   /* Get command reply payload from packet */
-  payload = silc_command_payload_parse(buffer->data, buffer->len);
+  payload = silc_command_payload_parse(silc_buffer_datalen(&packet->buffer));
+  silc_packet_free(packet);
   if (!payload) {
-    /* Silently ignore bad reply packet */
+    /** Bad reply payload */
     SILC_LOG_DEBUG(("Bad command reply packet"));
-    return;
-  }
-
-  /* Allocate command reply context. This must be free'd by the
-     command reply routine receiving it. */
-  ctx = silc_calloc(1, sizeof(*ctx));
-  ctx->users++;
-  ctx->client = client;
-  ctx->sock = sock;
-  ctx->payload = payload;
-  ctx->args = silc_command_get_args(ctx->payload);
-  ctx->packet = packet;
-  ctx->ident = silc_command_get_ident(ctx->payload);
-  silc_command_get_status(ctx->payload, &ctx->status, &ctx->error);
-
-  /* Check for pending commands and mark to be exeucted */
-  ctx->callbacks =
-    silc_client_command_pending_check(sock->user_data, ctx,
-                                     silc_command_get(ctx->payload),
-                                     ctx->ident, &ctx->callbacks_count);
-
-  /* Execute command reply */
-
-  command = silc_command_get(ctx->payload);
-
-  /* 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;
-    }
+    silc_fsm_next(fsm, silc_client_connection_st_packet);
+    return SILC_FSM_CONTINUE;
   }
 
-  if (cmd == SILC_LIST_END) {
-    if (reply)
-      /* No specific identifier for command reply, call first one found */
-      (*reply)(ctx, NULL);
-    else
-      silc_free(ctx);
+  cmd_ident = silc_command_get_ident(payload);
+  command = silc_command_get(payload);
+
+  /* Find the command pending reply */
+  silc_mutex_lock(conn->internal->lock);
+  silc_list_start(conn->internal->pending_commands);
+  while ((cmd = silc_list_get(conn->internal->pending_commands)))
+    if (cmd->cmd == command && cmd->cmd_ident == cmd_ident)
+      break;
+  silc_mutex_unlock(conn->internal->lock);
+
+  if (!cmd) {
+    /** Unknown command reply */
+    SILC_LOG_DEBUG(("Unknown command reply"));
+    silc_command_payload_free(payload);
+    silc_fsm_next(fsm, silc_client_connection_st_packet);
+    return SILC_FSM_CONTINUE;
   }
+
+  /* Signal command thread that command reply has arrived */
+  silc_fsm_set_state_context(&cmd->thread, payload);
+  silc_fsm_next(&cmd->thread, silc_client_command_reply_process);
+  silc_fsm_continue_sync(&cmd->thread);
+
+  /** Packet processed */
+  silc_fsm_next(fsm, silc_client_connection_st_packet);
+  return SILC_FSM_CONTINUE;
 }
 
-/* Duplicate Command Reply Context by adding reference counter. The context
-   won't be free'd untill it hits zero. */
+/* Wait here for command reply to arrive from remote host */
 
-SilcClientCommandReplyContext
-silc_client_command_reply_dup(SilcClientCommandReplyContext cmd)
+SILC_FSM_STATE(silc_client_command_reply_wait)
 {
-  cmd->users++;
-  SILC_LOG_DEBUG(("Command reply context %p refcnt %d->%d", cmd,
-                 cmd->users - 1, cmd->users));
-  return cmd;
+  SilcClientCommandContext cmd = fsm_context;
+
+  SILC_LOG_DEBUG(("Wait for command reply"));
+
+  /** Wait for command reply */
+  cmd->processed = FALSE;
+  silc_fsm_set_state_context(fsm, NULL);
+  silc_fsm_next_later(fsm, silc_client_command_reply_process, 20, 0);
+  return SILC_FSM_WAIT;
 }
 
-/* Free command reply context and its internals. */
+/* Process received command reply payload */
 
-void silc_client_command_reply_free(SilcClientCommandReplyContext cmd)
+SILC_FSM_STATE(silc_client_command_reply_process)
 {
-  cmd->users--;
-  SILC_LOG_DEBUG(("Command reply context %p refcnt %d->%d", cmd,
-                 cmd->users + 1, cmd->users));
-  if (cmd->users < 1) {
-    silc_command_payload_free(cmd->payload);
-    silc_free(cmd);
+  SilcClientCommandContext cmd = fsm_context;
+  SilcCommandPayload payload = state_context;
+
+  if (!payload) {
+    /* Timeout, reply not received in timely fashion */
+    SilcArgumentPayload args = NULL;
+    ERROR_CALLBACK(SILC_STATUS_ERR_TIMEDOUT);
+    return SILC_FSM_FINISH;
+  }
+
+  if (cmd->processed) {
+    /* Command reply processed */
+    silc_command_payload_free(payload);
+
+    if (cmd->status == SILC_STATUS_OK || cmd->status == SILC_STATUS_LIST_END ||
+       SILC_STATUS_IS_ERROR(cmd->status))
+      return SILC_FSM_FINISH;
+
+    /** Wait more command payloads */
+    silc_fsm_next(fsm, silc_client_command_reply_wait);
+    return SILC_FSM_CONTINUE;
+  }
+
+  silc_command_get_status(payload, &cmd->status, &cmd->error);
+  silc_fsm_set_state_context(fsm, payload);
+  cmd->processed = TRUE;
+
+  switch (cmd->cmd) {
+  case SILC_COMMAND_WHOIS:
+    /** WHOIS */
+    silc_fsm_next(fsm, silc_client_command_reply_whois);
+    break;
+  case SILC_COMMAND_WHOWAS:
+    /** WHOWAS */
+    silc_fsm_next(fsm, silc_client_command_reply_whowas);
+    break;
+  case SILC_COMMAND_IDENTIFY:
+    /** IDENTIFY */
+    silc_fsm_next(fsm, silc_client_command_reply_identify);
+    break;
+  case SILC_COMMAND_NICK:
+    /** NICK */
+    silc_fsm_next(fsm, silc_client_command_reply_nick);
+    break;
+  case SILC_COMMAND_LIST:
+    /** LIST */
+    silc_fsm_next(fsm, silc_client_command_reply_list);
+    break;
+  case SILC_COMMAND_TOPIC:
+    /** TOPIC */
+    silc_fsm_next(fsm, silc_client_command_reply_topic);
+    break;
+  case SILC_COMMAND_INVITE:
+    /** INVITE */
+    silc_fsm_next(fsm, silc_client_command_reply_invite);
+    break;
+  case SILC_COMMAND_QUIT:
+    /** QUIT */
+    silc_fsm_next(fsm, silc_client_command_reply_quit);
+    break;
+  case SILC_COMMAND_KILL:
+    /** KILL */
+    silc_fsm_next(fsm, silc_client_command_reply_kill);
+    break;
+  case SILC_COMMAND_INFO:
+    /** INFO */
+    silc_fsm_next(fsm, silc_client_command_reply_info);
+    break;
+  case SILC_COMMAND_STATS:
+    /** STATS */
+    silc_fsm_next(fsm, silc_client_command_reply_stats);
+    break;
+  case SILC_COMMAND_PING:
+    /** PING */
+    silc_fsm_next(fsm, silc_client_command_reply_ping);
+    break;
+  case SILC_COMMAND_OPER:
+    /** OPER */
+    silc_fsm_next(fsm, silc_client_command_reply_oper);
+    break;
+  case SILC_COMMAND_JOIN:
+    /** JOIN */
+    silc_fsm_next(fsm, silc_client_command_reply_join);
+    break;
+  case SILC_COMMAND_MOTD:
+    /** MOTD */
+    silc_fsm_next(fsm, silc_client_command_reply_motd);
+    break;
+  case SILC_COMMAND_UMODE:
+    /** UMODE */
+    silc_fsm_next(fsm, silc_client_command_reply_umode);
+    break;
+  case SILC_COMMAND_CMODE:
+    /** CMODE */
+    silc_fsm_next(fsm, silc_client_command_reply_cmode);
+    break;
+  case SILC_COMMAND_CUMODE:
+    /** CUMODE */
+    silc_fsm_next(fsm, silc_client_command_reply_cumode);
+    break;
+  case SILC_COMMAND_KICK:
+    /** kick */
+    silc_fsm_next(fsm, silc_client_command_reply_kick);
+    break;
+  case SILC_COMMAND_BAN:
+    /** BAN */
+    silc_fsm_next(fsm, silc_client_command_reply_ban);
+    break;
+  case SILC_COMMAND_DETACH:
+    /** DETACH */
+    silc_fsm_next(fsm, silc_client_command_reply_detach);
+    break;
+  case SILC_COMMAND_WATCH:
+    /** WATCH */
+    silc_fsm_next(fsm, silc_client_command_reply_watch);
+    break;
+  case SILC_COMMAND_SILCOPER:
+    /** SILCOPER */
+    silc_fsm_next(fsm, silc_client_command_reply_silcoper);
+    break;
+  case SILC_COMMAND_LEAVE:
+    /** LEAVE */
+    silc_fsm_next(fsm, silc_client_command_reply_leave);
+    break;
+  case SILC_COMMAND_USERS:
+    /** USERS */
+    silc_fsm_next(fsm, silc_client_command_reply_users);
+    break;
+  case SILC_COMMAND_GETKEY:
+    /** GETKEY */
+    silc_fsm_next(fsm, silc_client_command_reply_getkey);
+    break;
+  case SILC_COMMAND_SERVICE:
+    /** SERVICE */
+    silc_fsm_next(fsm, silc_client_command_reply_service);
+    break;
+  default:
+    return SILC_FSM_FINISH;
   }
+
+  return SILC_FSM_CONTINUE;
 }
 
-void
-silc_client_command_reply_whois_save(SilcClientCommandReplyContext cmd,
-                                    SilcStatus status,
-                                    SilcBool notify)
+/******************************** WHOIS *************************************/
+
+/* Received reply for WHOIS command. */
+
+SILC_FSM_STATE(silc_client_command_reply_whois)
 {
-  SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
-  SilcClientID *client_id;
+  SilcClientCommandContext cmd = fsm_context;
+  SilcClientConnection conn = cmd->conn;
+  SilcClient client = conn->client;
+  SilcCommandPayload payload = state_context;
+  SilcArgumentPayload args = silc_command_get_args(payload);
   SilcClientEntry client_entry = NULL;
-  SilcUInt32 len;
-  unsigned char *id_data, *tmp;
-  char *nickname = NULL, *username = NULL;
-  char *realname = NULL;
-  SilcUInt32 idle = 0, mode = 0;
+  SilcUInt32 idle = 0, mode = 0, fingerprint_len, len, *umodes = NULL;
   SilcBufferStruct channels, ch_user_modes;
-  SilcBool has_channels = FALSE, has_user_modes = FALSE;
-  unsigned char *fingerprint;
-  SilcUInt32 fingerprint_len;
+  SilcBool has_channels = FALSE;
+  SilcDList channel_list = NULL;
+  SilcID id;
+  char *nickname = NULL, *username = NULL, *realname = NULL;
+  unsigned char *fingerprint, *tmp;
 
-  id_data = silc_argument_get_arg_type(cmd->args, 2, &len);
-  if (!id_data) {
-    if (notify)
-      COMMAND_REPLY_ERROR(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
-    return;
-  }
+  CHECK_STATUS("WHOIS: ");
+  CHECK_ARGS(5, 11);
 
-  client_id = silc_id_payload_parse_id(id_data, len, NULL);
-  if (!client_id) {
-    if (notify)
-      COMMAND_REPLY_ERROR(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
-    return;
+  /* Get Client ID */
+  if (!silc_argument_get_decoded(args, 2, SILC_ARGUMENT_ID, &id, NULL)) {
+    ERROR_CALLBACK(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
+    goto out;
   }
 
-  nickname = silc_argument_get_arg_type(cmd->args, 3, &len);
-  username = silc_argument_get_arg_type(cmd->args, 4, &len);
-  realname = silc_argument_get_arg_type(cmd->args, 5, &len);
+  /* Get names */
+  nickname = silc_argument_get_arg_type(args, 3, NULL);
+  username = silc_argument_get_arg_type(args, 4, NULL);
+  realname = silc_argument_get_arg_type(args, 5, NULL);
   if (!nickname || !username || !realname) {
-    if (notify)
-      COMMAND_REPLY_ERROR(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
-    return;
+    ERROR_CALLBACK(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
+    goto out;
   }
 
-  tmp = silc_argument_get_arg_type(cmd->args, 6, &len);
+  /* Get joined channel list */
+  memset(&channels, 0, sizeof(channels));
+  tmp = silc_argument_get_arg_type(args, 6, &len);
   if (tmp) {
-    silc_buffer_set(&channels, tmp, len);
     has_channels = TRUE;
+    silc_buffer_set(&channels, tmp, len);
+
+    /* Get channel user mode list */
+    tmp = silc_argument_get_arg_type(args, 10, &len);
+    if (!tmp) {
+      ERROR_CALLBACK(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
+      goto out;
+    }
+    silc_buffer_set(&ch_user_modes, tmp, len);
   }
 
-  tmp = silc_argument_get_arg_type(cmd->args, 7, &len);
+  /* Get user mode */
+  tmp = silc_argument_get_arg_type(args, 7, &len);
   if (tmp)
     SILC_GET32_MSB(mode, tmp);
 
-  tmp = silc_argument_get_arg_type(cmd->args, 8, &len);
+  /* Get idle time */
+  tmp = silc_argument_get_arg_type(args, 8, &len);
   if (tmp)
     SILC_GET32_MSB(idle, tmp);
 
-  fingerprint = silc_argument_get_arg_type(cmd->args, 9, &fingerprint_len);
-
-  tmp = silc_argument_get_arg_type(cmd->args, 10, &len);
-  if (tmp) {
-    silc_buffer_set(&ch_user_modes, tmp, len);
-    has_user_modes = TRUE;
-  }
+  /* Get fingerprint */
+  fingerprint = silc_argument_get_arg_type(args, 9, &fingerprint_len);
 
   /* Check if we have this client cached already. */
-  client_entry = silc_client_get_client_by_id(cmd->client, conn, client_id);
+  client_entry = silc_client_get_client_by_id(client, conn, &id.u.client_id);
   if (!client_entry) {
-    SILC_LOG_DEBUG(("Adding new client entry"));
+    SILC_LOG_DEBUG(("Adding new client entry (WHOIS)"));
     client_entry =
-      silc_client_add_client(cmd->client, conn, nickname, username, realname,
-                            client_id, mode);
+      silc_client_add_client(client, conn, nickname, username, realname,
+                            &id.u.client_id, mode);
     if (!client_entry) {
-      if (notify)
-       COMMAND_REPLY_ERROR(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
-      return;
+      ERROR_CALLBACK(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
+      goto out;
     }
   } else {
-    silc_client_update_client(cmd->client, conn, client_entry,
+    silc_client_update_client(client, conn, client_entry,
                              nickname, username, realname, mode);
-    silc_free(client_id);
   }
 
-  if (fingerprint && !client_entry->fingerprint) {
-    client_entry->fingerprint = silc_memdup(fingerprint, fingerprint_len);
-    client_entry->fingerprint_len = fingerprint_len;
-  }
+  if (fingerprint && fingerprint_len == sizeof(client_entry->fingerprint))
+    memcpy(client_entry->fingerprint, fingerprint, fingerprint_len);
 
-  /* Take Requested Attributes if set. */
-  tmp = silc_argument_get_arg_type(cmd->args, 11, &len);
+  /* Get user attributes */
+  tmp = silc_argument_get_arg_type(args, 11, &len);
   if (tmp) {
     if (client_entry->attrs)
       silc_attribute_payload_list_free(client_entry->attrs);
     client_entry->attrs = silc_attribute_payload_parse(tmp, len);
   }
 
-  client_entry->status &= ~SILC_CLIENT_STATUS_RESOLVING;
+  /* Parse channel and channel user mode list */
+  if (has_channels) {
+    channel_list = silc_channel_payload_parse_list(silc_buffer_data(&channels),
+                                                  silc_buffer_len(&channels));
+    if (channel_list)
+      silc_get_mode_list(&ch_user_modes, silc_dlist_count(channel_list),
+                        &umodes);
+  }
 
   /* Notify application */
-  if (!cmd->callbacks_count && notify)
-    COMMAND_REPLY((SILC_ARGS, client_entry, nickname, username, realname,
-                  has_channels ? &channels : NULL, mode, idle,
-                  fingerprint, has_user_modes ? &ch_user_modes : NULL,
-                  client_entry->attrs));
-}
+  silc_client_command_callback(cmd, client_entry, nickname, username,
+                              realname, channel_list, mode, idle, fingerprint,
+                              umodes, client_entry->attrs);
 
-/* Received reply for WHOIS command. This maybe called several times
-   for one WHOIS command as server may reply with list of results. */
-
-SILC_CLIENT_CMD_REPLY_FUNC(whois)
-{
-  SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
-  SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
-
-  COMMAND_CHECK_STATUS;
-
-  /* Save WHOIS info */
-  silc_client_command_reply_whois_save(cmd, cmd->status, TRUE);
-
-  /* Pending callbacks are not executed if this was an list entry */
-  if (cmd->status != SILC_STATUS_OK &&
-      cmd->status != SILC_STATUS_LIST_END) {
-    silc_client_command_reply_free(cmd);
-    return;
+  if (has_channels) {
+    silc_dlist_uninit(channel_list);
+    silc_free(umodes);
   }
 
  out:
-  SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_WHOIS);
-
- err:
-  /* If we received notify for invalid ID we'll remove the ID if we
-     have it cached. */
-  if (cmd->error == SILC_STATUS_ERR_NO_SUCH_CLIENT_ID) {
-    SilcClientEntry client_entry;
-    SilcUInt32 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, NULL);
-      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);
+  silc_fsm_next(fsm, silc_client_command_reply_process);
+  return SILC_FSM_CONTINUE;
 }
 
+/******************************** WHOWAS ************************************/
+
 /* Received reply for WHOWAS command. */
 
-SILC_CLIENT_CMD_REPLY_FUNC(whowas)
+SILC_FSM_STATE(silc_client_command_reply_whowas)
 {
-  SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
-  SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
-  SilcClientID *client_id;
-  SilcClientEntry client_entry = NULL;
-  SilcUInt32 len;
-  unsigned char *id_data;
+  SilcClientCommandContext cmd = fsm_context;
+  SilcClientConnection conn = cmd->conn;
+  SilcClient client = conn->client;
+  SilcCommandPayload payload = state_context;
+  SilcArgumentPayload args = silc_command_get_args(payload);
+  SilcClientEntry client_entry;
+  SilcID id;
   char *nickname, *username;
   char *realname = NULL;
 
-  COMMAND_CHECK_STATUS;
+  CHECK_STATUS("WHOWAS: ");
+  CHECK_ARGS(4, 5);
 
-  id_data = silc_argument_get_arg_type(cmd->args, 2, &len);
-  if (!id_data) {
-    COMMAND_REPLY_ERROR(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
-    goto out;
-  }
-
-  client_id = silc_id_payload_parse_id(id_data, len, NULL);
-  if (!client_id) {
-    COMMAND_REPLY_ERROR(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
+  /* Get Client ID */
+  if (!silc_argument_get_decoded(args, 2, SILC_ARGUMENT_ID, &id, NULL)) {
+    ERROR_CALLBACK(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
     goto out;
   }
 
-  /* Get the client entry, if exists */
-  client_entry = silc_client_get_client_by_id(cmd->client, conn, client_id);
-  silc_free(client_id);
+  /* Get the client entry */
+  client_entry = silc_client_get_client_by_id(client, conn, &id.u.client_id);
 
-  nickname = silc_argument_get_arg_type(cmd->args, 3, &len);
-  username = silc_argument_get_arg_type(cmd->args, 4, &len);
-  realname = silc_argument_get_arg_type(cmd->args, 5, &len);
+  /* Get names */
+  nickname = silc_argument_get_arg_type(args, 3, NULL);
+  username = silc_argument_get_arg_type(args, 4, NULL);
+  realname = silc_argument_get_arg_type(args, 5, NULL);
   if (!nickname || !username) {
-    COMMAND_REPLY_ERROR(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
+    ERROR_CALLBACK(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
     goto out;
   }
 
   /* Notify application. We don't save any history information to any
-     cache. Just pass the data to the application for displaying on
-     the screen. */
-  COMMAND_REPLY((SILC_ARGS, client_entry, nickname, username, realname));
-
-  /* Pending callbacks are not executed if this was an list entry */
-  if (cmd->status != SILC_STATUS_OK &&
-      cmd->status != SILC_STATUS_LIST_END) {
-    silc_client_command_reply_free(cmd);
-    return;
-  }
+     cache. Just pass the data to the application. */
+  silc_client_command_callback(cmd, client_entry, nickname, username,
+                              realname);
 
  out:
-  SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_WHOWAS);
- err:
-  silc_client_command_reply_free(cmd);
+  silc_fsm_next(fsm, silc_client_command_reply_process);
+  return SILC_FSM_CONTINUE;
 }
 
-static void
-silc_client_command_reply_identify_save(SilcClientCommandReplyContext cmd,
-                                       SilcStatus status,
-                                       SilcBool notify)
+/******************************** IDENTIFY **********************************/
+
+/* Received reply for IDENTIFY command. */
+
+SILC_FSM_STATE(silc_client_command_reply_identify)
 {
-  SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
-  SilcClient client = cmd->client;
-  SilcClientID *client_id = NULL;
-  SilcServerID *server_id = NULL;
-  SilcChannelID *channel_id = NULL;
+  SilcClientCommandContext cmd = fsm_context;
+  SilcClientConnection conn = cmd->conn;
+  SilcClient client = conn->client;
+  SilcCommandPayload payload = state_context;
+  SilcArgumentPayload args = silc_command_get_args(payload);
   SilcClientEntry client_entry;
   SilcServerEntry server_entry;
   SilcChannelEntry channel_entry;
   SilcUInt32 len;
-  unsigned char *id_data;
+  SilcID id;
   char *name = NULL, *info = NULL;
-  SilcIDPayload idp = NULL;
-  SilcIdType id_type;
 
-  id_data = silc_argument_get_arg_type(cmd->args, 2, &len);
-  if (!id_data) {
-    if (notify)
-      COMMAND_REPLY_ERROR(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
-    return;
-  }
-  idp = silc_id_payload_parse(id_data, len);
-  if (!idp) {
-    if (notify)
-      COMMAND_REPLY_ERROR(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
-    return;
-  }
+  CHECK_STATUS("IDENTIFY: ");
+  CHECK_ARGS(2, 4);
 
-  name = silc_argument_get_arg_type(cmd->args, 3, &len);
-  info = silc_argument_get_arg_type(cmd->args, 4, &len);
+  /* Get the ID */
+  if (!silc_argument_get_decoded(args, 2, SILC_ARGUMENT_ID, &id, NULL)) {
+    ERROR_CALLBACK(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
+    goto out;
+  }
 
-  id_type = silc_id_payload_get_type(idp);
+  /* Get names */
+  name = silc_argument_get_arg_type(args, 3, &len);
+  info = silc_argument_get_arg_type(args, 4, &len);
 
-  switch (id_type) {
+  switch (id.type) {
   case SILC_ID_CLIENT:
-    client_id = silc_id_payload_get_id(idp);
-
     SILC_LOG_DEBUG(("Received client information"));
 
     /* Check if we have this client cached already. */
-    client_entry = silc_client_get_client_by_id(cmd->client, conn, client_id);
+    client_entry = silc_client_get_client_by_id(client, conn, &id.u.client_id);
     if (!client_entry) {
-      SILC_LOG_DEBUG(("Adding new client entry"));
+      SILC_LOG_DEBUG(("Adding new client entry (IDENTIFY)"));
       client_entry =
-       silc_client_add_client(cmd->client, conn, name, info, NULL,
-                              silc_id_dup(client_id, id_type), 0);
+       silc_client_add_client(client, conn, name, info, NULL,
+                              &id.u.client_id, 0);
       if (!client_entry) {
-       if (notify)
-         COMMAND_REPLY_ERROR(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
-       return;
+       ERROR_CALLBACK(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
+       goto out;
       }
     } else {
-      silc_client_update_client(cmd->client, conn, client_entry,
+      silc_client_update_client(client, conn, client_entry,
                                name, info, NULL, 0);
     }
 
-    client_entry->status &= ~SILC_CLIENT_STATUS_RESOLVING;
-
     /* Notify application */
-    if (notify)
-      COMMAND_REPLY((SILC_ARGS, client_entry, name, info));
+    silc_client_command_callback(cmd, client_entry, name, info);
     break;
 
   case SILC_ID_SERVER:
-    server_id = silc_id_payload_get_id(idp);
-
     SILC_LOG_DEBUG(("Received server information"));
 
     /* Check if we have this server cached already. */
-    server_entry = silc_client_get_server_by_id(cmd->client, conn, server_id);
+    server_entry = silc_client_get_server_by_id(client, conn, &id.u.server_id);
     if (!server_entry) {
-      SILC_LOG_DEBUG(("Adding new server entry"));
-      server_entry = silc_client_add_server(cmd->client, conn, name, info,
-                                           silc_id_dup(server_id, id_type));
+      SILC_LOG_DEBUG(("Adding new server entry (IDENTIFY)"));
+      server_entry = silc_client_add_server(client, conn, name, info,
+                                           &id.u.server_id);
       if (!server_entry) {
-       if (notify)
-         COMMAND_REPLY_ERROR(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
-       return;
+       ERROR_CALLBACK(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
+       goto out;
       }
     } else {
       silc_client_update_server(client, conn, server_entry, name, info);
     }
-
     server_entry->resolve_cmd_ident = 0;
 
     /* Notify application */
-    if (notify)
-      COMMAND_REPLY((SILC_ARGS, server_entry, name, info));
+    silc_client_command_callback(cmd, server_entry, name, info);
     break;
 
   case SILC_ID_CHANNEL:
-    channel_id = silc_id_payload_get_id(idp);
-
     SILC_LOG_DEBUG(("Received channel information"));
 
     /* Check if we have this channel cached already. */
-    channel_entry = silc_client_get_channel_by_id(client, conn, channel_id);
+    channel_entry = silc_client_get_channel_by_id(client, conn,
+                                                 &id.u.channel_id);
     if (!channel_entry) {
-      if (!name)
-       break;
+      SILC_LOG_DEBUG(("Adding new channel entry (IDENTIFY"));
+
+      if (!name) {
+       ERROR_CALLBACK(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
+       goto out;
+      }
 
       /* Add new channel entry */
       channel_entry = silc_client_add_channel(client, conn, name, 0,
-                                             channel_id);
+                                             &id.u.channel_id);
       if (!channel_entry) {
-       if (notify)
-         COMMAND_REPLY_ERROR(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
-       return;
+       ERROR_CALLBACK(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
+       goto out;
       }
-      channel_id = NULL;
     }
 
     /* Notify application */
-    if (notify)
-      COMMAND_REPLY((SILC_ARGS, channel_entry, name, info));
+    silc_client_command_callback(cmd, channel_entry, name, info);
     break;
   }
 
-  silc_id_payload_free(idp);
-  silc_free(client_id);
-  silc_free(server_id);
-  silc_free(channel_id);
-}
-
-/* Received reply for IDENTIFY command. This maybe called several times
-   for one IDENTIFY command as server may reply with list of results.
-   This is totally silent and does not print anything on screen. */
-
-SILC_CLIENT_CMD_REPLY_FUNC(identify)
-{
-  SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
-  SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
-
-  COMMAND_CHECK_STATUS;
-
-  /* Save IDENTIFY info */
-  silc_client_command_reply_identify_save(cmd, cmd->status, TRUE);
-
-  /* Pending callbacks are not executed if this was an list entry */
-  if (cmd->status != SILC_STATUS_OK &&
-      cmd->status != SILC_STATUS_LIST_END) {
-    silc_client_command_reply_free(cmd);
-    return;
-  }
-
  out:
-  SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_IDENTIFY);
-
- err:
-  /* If we received notify for invalid ID we'll remove the ID if we
-     have it cached. */
-  if (cmd->error == SILC_STATUS_ERR_NO_SUCH_CLIENT_ID) {
-    SilcClientEntry client_entry;
-    SilcUInt32 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, NULL);
-      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);
+  silc_fsm_next(fsm, silc_client_command_reply_process);
+  return SILC_FSM_CONTINUE;
 }
 
-/* Received reply for command NICK. If everything went without errors
-   we just received our new Client ID. */
+/********************************** NICK ************************************/
 
-SILC_CLIENT_CMD_REPLY_FUNC(nick)
+/* Received reply for command NICK. */
+
+SILC_FSM_STATE(silc_client_command_reply_nick)
 {
-  SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
-  SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
-  SilcIDPayload idp;
-  unsigned char *tmp;
-  SilcUInt32 argc, len;
+  SilcClientCommandContext cmd = fsm_context;
+  SilcClientConnection conn = cmd->conn;
+  SilcClient client = conn->client;
+  SilcCommandPayload payload = state_context;
+  SilcArgumentPayload args = silc_command_get_args(payload);
+  unsigned char *tmp, *nick, *idp;
+  SilcUInt32 len, idp_len;
   SilcClientID old_client_id;
+  SilcID id;
 
-  SILC_LOG_DEBUG(("Start"));
-
-  if (cmd->error != SILC_STATUS_OK) {
-    SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_ERROR,
-       "Cannot set nickname: %s",
-       silc_get_status_message(cmd->error));
-    COMMAND_REPLY_ERROR(cmd->error);
-    goto out;
-  }
-
-  argc = silc_argument_get_arg_num(cmd->args);
-  if (argc < 2 || argc > 3) {
-    SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_ERROR,
-       "Cannot set nickname: bad reply to command");
-    COMMAND_REPLY_ERROR(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
-    goto out;
-  }
+  /* Sanity checks */
+  CHECK_STATUS("Cannot set nickname: ");
+  CHECK_ARGS(2, 3);
 
-  /* Save old Client ID */
   old_client_id = *conn->local_id;
 
   /* Take received Client ID */
-  tmp = silc_argument_get_arg_type(cmd->args, 2, &len);
-  idp = silc_id_payload_parse(tmp, len);
+  idp = silc_argument_get_arg_type(args, 2, &idp_len);
   if (!idp) {
-    COMMAND_REPLY_ERROR(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
+    ERROR_CALLBACK(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
+    goto out;
+  }
+  if (!silc_id_payload_parse_id(idp, idp_len, &id)) {
+    ERROR_CALLBACK(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
     goto out;
   }
-  silc_client_receive_new_id(cmd->client, cmd->sock, idp);
 
-  /* Take the new nickname too */
-  tmp = silc_argument_get_arg_type(cmd->args, 3, &len);
-  if (tmp) {
-    silc_idcache_del_by_context(conn->internal->client_cache,
-                               conn->local_entry);
-    if (conn->nickname)
-      silc_free(conn->nickname);
-    conn->nickname = strdup(tmp);
-    conn->local_entry->nickname = conn->nickname;
-
-    /* Normalize nickname */
-    tmp = silc_identifier_check(conn->nickname, strlen(conn->nickname),
-                               SILC_STRING_UTF8, 128, NULL);
-    if (!tmp) {
-      COMMAND_REPLY_ERROR(SILC_STATUS_ERR_BAD_NICKNAME);
-      goto out;
-    }
+  /* Take the new nickname */
+  nick = silc_argument_get_arg_type(args, 3, &len);
+  if (!nick) {
+    ERROR_CALLBACK(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
+    goto out;
+  }
 
-    silc_client_nickname_format(cmd->client, conn, conn->local_entry);
+  /* Normalize nickname */
+  tmp = silc_identifier_check(nick, len, SILC_STRING_UTF8, 128, NULL);
+  if (!tmp) {
+    ERROR_CALLBACK(SILC_STATUS_ERR_BAD_NICKNAME);
+    goto out;
+  }
 
-    silc_idcache_add(conn->internal->client_cache, tmp,
-                    conn->local_entry->id, conn->local_entry, 0, NULL);
+  /* Update the client entry */
+  if (!silc_idcache_update(conn->internal->client_cache,
+                          conn->internal->local_entry,
+                          &conn->local_entry->id,
+                          &id.u.client_id,
+                          conn->local_entry->nickname_normalized,
+                          tmp, TRUE)) {
+    silc_free(tmp);
+    ERROR_CALLBACK(SILC_STATUS_ERR_BAD_NICKNAME);
+    goto out;
   }
+  memcpy(conn->local_entry->nickname, nick, strlen(nick));
+  conn->local_entry->nickname_normalized = tmp;
+  silc_buffer_enlarge(conn->local_idp, idp_len);
+  silc_buffer_put(conn->local_idp, idp, idp_len);
+  silc_client_nickname_format(client, conn, conn->local_entry);
 
   /* Notify application */
-  COMMAND_REPLY((SILC_ARGS, conn->local_entry, conn->local_entry->nickname,
-                (const SilcClientID *)&old_client_id));
+  silc_client_command_callback(cmd, conn->local_entry,
+                              conn->local_entry->nickname, &old_client_id);
 
  out:
-  SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_NICK);
-  silc_client_command_reply_free(cmd);
+  silc_fsm_next(fsm, silc_client_command_reply_process);
+  return SILC_FSM_CONTINUE;
 }
 
+/********************************** LIST ************************************/
+
 /* Received reply to the LIST command. */
 
-SILC_CLIENT_CMD_REPLY_FUNC(list)
+SILC_FSM_STATE(silc_client_command_reply_list)
 {
-  SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
-  SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
+  SilcClientCommandContext cmd = fsm_context;
+  SilcClientConnection conn = cmd->conn;
+  SilcClient client = conn->client;
+  SilcCommandPayload payload = state_context;
+  SilcArgumentPayload args = silc_command_get_args(payload);
   unsigned char *tmp, *name, *topic;
-  SilcUInt32 usercount = 0, len;
-  SilcChannelID *channel_id = NULL;
+  SilcUInt32 usercount = 0;
   SilcChannelEntry channel_entry;
+  SilcID id;
 
-  COMMAND_CHECK_STATUS;
+  /* Sanity checks */
+  CHECK_STATUS("Cannot list channels: ");
 
-  tmp = silc_argument_get_arg_type(cmd->args, 2, &len);
-  if (!tmp) {
+  if (!silc_argument_get_decoded(args, 2, SILC_ARGUMENT_ID, &id, NULL)) {
     /* There were no channels in the network. */
-    COMMAND_REPLY((SILC_ARGS, NULL, NULL, 0));
-    goto out;
+    silc_client_command_callback(cmd, NULL, NULL, NULL, 0);
+    silc_fsm_next(fsm, silc_client_command_reply_process);
+    return SILC_FSM_CONTINUE;
   }
 
-  channel_id = silc_id_payload_parse_id(tmp, len, NULL);
-  if (!channel_id) {
-    COMMAND_REPLY_ERROR(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
-    goto out;
-  }
+  CHECK_ARGS(3, 5);
 
-  name = silc_argument_get_arg_type(cmd->args, 3, NULL);
+  name = silc_argument_get_arg_type(args, 3, NULL);
   if (!name) {
-    COMMAND_REPLY_ERROR(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
+    ERROR_CALLBACK(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
     goto out;
   }
 
-  topic = silc_argument_get_arg_type(cmd->args, 4, NULL);
-  tmp = silc_argument_get_arg_type(cmd->args, 5, NULL);
+  topic = silc_argument_get_arg_type(args, 4, NULL);
+  tmp = silc_argument_get_arg_type(args, 5, NULL);
   if (tmp)
     SILC_GET32_MSB(usercount, tmp);
 
   /* Check whether the channel exists, and add it to cache if it doesn't. */
-  channel_entry = silc_client_get_channel_by_id(cmd->client, conn,
-                                               channel_id);
+  channel_entry = silc_client_get_channel_by_id(client, conn,
+                                               &id.u.channel_id);
   if (!channel_entry) {
     /* Add new channel entry */
-    channel_entry = silc_client_add_channel(cmd->client, conn, name, 0,
-                                           channel_id);
+    channel_entry = silc_client_add_channel(client, conn, name, 0,
+                                           &id.u.channel_id);
     if (!channel_entry) {
-      COMMAND_REPLY_ERROR(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
+      ERROR_CALLBACK(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
       goto out;
     }
-    channel_id = NULL;
   }
 
   /* Notify application */
-  COMMAND_REPLY((SILC_ARGS, channel_entry, name, topic, usercount));
-
-  /* Pending callbacks are not executed if this was an list entry */
-  if (cmd->status != SILC_STATUS_OK &&
-      cmd->status != SILC_STATUS_LIST_END) {
-    silc_client_command_reply_free(cmd);
-    return;
-  }
+  silc_client_command_callback(cmd, channel_entry, name, topic, usercount);
 
  out:
-  silc_free(channel_id);
-  SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_LIST);
- err:
-  silc_client_command_reply_free(cmd);
+  silc_fsm_next(fsm, silc_client_command_reply_process);
+  return SILC_FSM_CONTINUE;
 }
 
+/********************************* TOPIC ************************************/
+
 /* Received reply to topic command. */
 
-SILC_CLIENT_CMD_REPLY_FUNC(topic)
+SILC_FSM_STATE(silc_client_command_reply_topic)
 {
-  SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
-  SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
+  SilcClientCommandContext cmd = fsm_context;
+  SilcClientConnection conn = cmd->conn;
+  SilcClient client = conn->client;
+  SilcCommandPayload payload = state_context;
+  SilcArgumentPayload args = silc_command_get_args(payload);
   SilcChannelEntry channel;
-  SilcChannelID *channel_id = NULL;
-  unsigned char *tmp;
   char *topic;
-  SilcUInt32 argc, len;
-
-  if (cmd->error != SILC_STATUS_OK) {
-    SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_ERROR,
-       "Cannot set topic: %s", silc_get_status_message(cmd->error));
-    COMMAND_REPLY_ERROR(cmd->error);
-    goto out;
-  }
+  SilcUInt32 len;
+  SilcID id;
 
-  argc = silc_argument_get_arg_num(cmd->args);
-  if (argc < 1 || argc > 3) {
-    COMMAND_REPLY_ERROR(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
-    goto out;
-  }
+  /* Sanity checks */
+  CHECK_STATUS("Cannot set topic: ");
+  CHECK_ARGS(2, 3);
 
   /* Take Channel ID */
-  tmp = silc_argument_get_arg_type(cmd->args, 2, &len);
-  if (!tmp)
-    goto out;
-
-  /* Take topic */
-  topic = silc_argument_get_arg_type(cmd->args, 3, NULL);
-  if (!topic)
-    goto out;
-
-  channel_id = silc_id_payload_parse_id(tmp, len, NULL);
-  if (!channel_id)
+  if (!silc_argument_get_decoded(args, 2, SILC_ARGUMENT_ID, &id, NULL)) {
+    ERROR_CALLBACK(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
     goto out;
+  }
 
   /* Get the channel entry */
-  channel = silc_client_get_channel_by_id(cmd->client, conn, channel_id);
+  channel = silc_client_get_channel_by_id(client, conn, &id.u.channel_id);
   if (!channel) {
-    silc_free(channel_id);
-    COMMAND_REPLY_ERROR(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
+    ERROR_CALLBACK(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
     goto out;
   }
 
+  /* Take topic */
+  topic = silc_argument_get_arg_type(args, 3, &len);
   if (topic) {
     silc_free(channel->topic);
-    channel->topic = silc_memdup(topic, strlen(topic));
+    channel->topic = silc_memdup(topic, len);
   }
 
   /* Notify application */
-  COMMAND_REPLY((SILC_ARGS, channel, topic));
+  silc_client_command_callback(cmd, channel, channel->topic);
 
  out:
-  SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_TOPIC);
-  silc_client_command_reply_free(cmd);
+  silc_fsm_next(fsm, silc_client_command_reply_process);
+  return SILC_FSM_CONTINUE;
 }
 
+/********************************* INVITE ***********************************/
+
 /* Received reply to invite command. */
 
-SILC_CLIENT_CMD_REPLY_FUNC(invite)
+SILC_FSM_STATE(silc_client_command_reply_invite)
 {
-  SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
-  SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
+  SilcClientCommandContext cmd = fsm_context;
+  SilcClientConnection conn = cmd->conn;
+  SilcClient client = conn->client;
+  SilcCommandPayload payload = state_context;
+  SilcArgumentPayload args = silc_command_get_args(payload);
   SilcChannelEntry channel;
-  SilcChannelID *channel_id;
   unsigned char *tmp;
   SilcUInt32 len;
   SilcBufferStruct buf;
+  SilcID id;
 
-  if (cmd->error != SILC_STATUS_OK) {
-    SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_ERROR,
-       "Cannot invite: %s", silc_get_status_message(cmd->error));
-    COMMAND_REPLY_ERROR(cmd->error);
-    goto out;
-  }
+  /* Sanity checks */
+  CHECK_STATUS("Cannot invite: ");
+  CHECK_ARGS(2, 3);
 
   /* Take Channel ID */
-  tmp = silc_argument_get_arg_type(cmd->args, 2, &len);
-  if (!tmp)
-    goto out;
-
-  channel_id = silc_id_payload_parse_id(tmp, len, NULL);
-  if (!channel_id)
+  if (!silc_argument_get_decoded(args, 2, SILC_ARGUMENT_ID, &id, NULL)) {
+    ERROR_CALLBACK(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
     goto out;
+  }
 
   /* Get the channel entry */
-  channel = silc_client_get_channel_by_id(cmd->client, conn, channel_id);
+  channel = silc_client_get_channel_by_id(client, conn, &id.u.channel_id);
   if (!channel) {
-    silc_free(channel_id);
-    COMMAND_REPLY_ERROR(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
+    ERROR_CALLBACK(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
     goto out;
   }
 
   /* Get the invite list */
-  tmp = silc_argument_get_arg_type(cmd->args, 3, &len);
+  tmp = silc_argument_get_arg_type(args, 3, &len);
   if (tmp)
     silc_buffer_set(&buf, tmp, len);
 
   /* Notify application */
-  COMMAND_REPLY((SILC_ARGS, channel, tmp ? &buf : NULL));
+  silc_client_command_callback(cmd, channel, tmp ? &buf : NULL);
 
  out:
-  SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_INVITE);
-  silc_client_command_reply_free(cmd);
+  silc_fsm_next(fsm, silc_client_command_reply_process);
+  return SILC_FSM_CONTINUE;
 }
 
+/********************************** KILL ************************************/
+
 /* Received reply to the KILL command. */
 
-SILC_CLIENT_CMD_REPLY_FUNC(kill)
+SILC_FSM_STATE(silc_client_command_reply_kill)
 {
-  SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
-  SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
-  SilcClientID *client_id;
-  SilcClientEntry client_entry = NULL;
-  SilcUInt32 len;
-  unsigned char *id_data;
+  SilcClientCommandContext cmd = fsm_context;
+  SilcClientConnection conn = cmd->conn;
+  SilcClient client = conn->client;
+  SilcCommandPayload payload = state_context;
+  SilcArgumentPayload args = silc_command_get_args(payload);
+  SilcClientEntry client_entry;
+  SilcID id;
+
+  /* Sanity checks */
+  CHECK_STATUS("Cannot kill: ");
+  CHECK_ARGS(2, 2);
 
-  if (cmd->error != SILC_STATUS_OK) {
-    SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_ERROR,
-       "Cannot kill: %s", silc_get_status_message(cmd->error));
-    COMMAND_REPLY_ERROR(cmd->error);
+  if (!silc_argument_get_decoded(args, 2, SILC_ARGUMENT_ID, &id, NULL)) {
+    ERROR_CALLBACK(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
     goto out;
   }
 
-  id_data = silc_argument_get_arg_type(cmd->args, 2, &len);
-  if (id_data) {
-    client_id = silc_id_payload_parse_id(id_data, len, NULL);
-    if (!client_id) {
-      COMMAND_REPLY_ERROR(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
-      goto out;
-    }
-
-    /* Get the client entry, if exists */
-    client_entry = silc_client_get_client_by_id(cmd->client, conn, client_id);
-    silc_free(client_id);
-  }
+  /* Get the client entry, if exists */
+  client_entry = silc_client_get_client_by_id(client, conn, &id.u.client_id);
 
   /* Notify application */
-  COMMAND_REPLY((SILC_ARGS, client_entry));
+  silc_client_command_callback(cmd, client_entry);
+
+  /* Remove the client from all channels and free it */
+  if (client_entry)
+    silc_client_del_client(client, conn, client_entry);
 
  out:
-  SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_KILL);
-  silc_client_command_reply_free(cmd);
+  silc_fsm_next(fsm, silc_client_command_reply_process);
+  return SILC_FSM_CONTINUE;
 }
 
+/********************************** INFO ************************************/
+
 /* Received reply to INFO command. We receive the server ID and some
    information about the server user requested. */
 
-SILC_CLIENT_CMD_REPLY_FUNC(info)
+SILC_FSM_STATE(silc_client_command_reply_info)
 {
-  SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
-  SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
-  unsigned char *tmp;
+  SilcClientCommandContext cmd = fsm_context;
+  SilcClientConnection conn = cmd->conn;
+  SilcClient client = conn->client;
+  SilcCommandPayload payload = state_context;
+  SilcArgumentPayload args = silc_command_get_args(payload);
   SilcServerEntry server;
-  SilcServerID *server_id = NULL;
   char *server_name, *server_info;
-  SilcUInt32 len;
-
-  SILC_LOG_DEBUG(("Start"));
+  SilcID id;
 
-  if (cmd->error != SILC_STATUS_OK) {
-    SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_ERROR, "%s",
-       silc_get_status_message(cmd->error));
-    COMMAND_REPLY_ERROR(cmd->error);
-    goto out;
-  }
+  /* Sanity checks */
+  CHECK_STATUS("Cannot get info: ");
+  CHECK_ARGS(4, 4);
 
   /* 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, NULL);
-  if (!server_id)
+  if (!silc_argument_get_decoded(args, 2, SILC_ARGUMENT_ID, &id, NULL)) {
+    ERROR_CALLBACK(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
     goto out;
+  }
 
   /* Get server name */
-  server_name = silc_argument_get_arg_type(cmd->args, 3, NULL);
-  if (!server_name)
+  server_name = silc_argument_get_arg_type(args, 3, NULL);
+  if (!server_name) {
+    ERROR_CALLBACK(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
     goto out;
+  }
 
   /* Get server info */
-  server_info = silc_argument_get_arg_type(cmd->args, 4, NULL);
-  if (!server_info)
+  server_info = silc_argument_get_arg_type(args, 4, NULL);
+  if (!server_info) {
+    ERROR_CALLBACK(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
     goto out;
+  }
 
   /* See whether we have this server cached. If not create it. */
-  server = silc_client_get_server_by_id(cmd->client, conn, server_id);
+  server = silc_client_get_server_by_id(client, conn, &id.u.server_id);
   if (!server) {
     SILC_LOG_DEBUG(("New server entry"));
-    server = silc_client_add_server(cmd->client, conn, server_name,
-                                   server_info,
-                                   silc_id_dup(server_id, SILC_ID_SERVER));
+    server = silc_client_add_server(client, conn, server_name,
+                                   server_info, &id.u.server_id);
     if (!server)
       goto out;
   }
 
   /* Notify application */
-  COMMAND_REPLY((SILC_ARGS, server, server->server_name, server->server_info));
+  silc_client_command_callback(cmd, server, server->server_name,
+                              server->server_info);
 
  out:
-  SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_INFO);
-  silc_free(server_id);
-  silc_client_command_reply_free(cmd);
+  silc_fsm_next(fsm, silc_client_command_reply_process);
+  return SILC_FSM_CONTINUE;
 }
 
+/********************************** STATS ***********************************/
+
 /* Received reply to STATS command. */
 
-SILC_CLIENT_CMD_REPLY_FUNC(stats)
+SILC_FSM_STATE(silc_client_command_reply_stats)
 {
-  SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
-  SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
-  unsigned char *tmp, *buf = NULL;
-  SilcUInt32 len, buf_len = 0;
-
-  if (cmd->error != SILC_STATUS_OK) {
-    SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_ERROR,
-       "%s", silc_get_status_message(cmd->error));
-    COMMAND_REPLY_ERROR(cmd->error);
-    goto out;
-  }
+  SilcClientCommandContext cmd = fsm_context;
+  SilcCommandPayload payload = state_context;
+  SilcArgumentPayload args = silc_command_get_args(payload);
+  SilcClientStats stats;
+  unsigned char *buf = NULL;
+  SilcUInt32 buf_len = 0;
+  SilcBufferStruct b;
+  SilcID id;
+
+  /* Sanity checks */
+  CHECK_STATUS("Cannot get stats: ");
+  CHECK_ARGS(2, 3);
 
   /* Get server ID */
-  tmp = silc_argument_get_arg_type(cmd->args, 2, &len);
-  if (!tmp)
+  if (!silc_argument_get_decoded(args, 2, SILC_ARGUMENT_ID, &id, NULL)) {
+    ERROR_CALLBACK(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
     goto out;
+  }
 
   /* Get statistics structure */
-  buf = silc_argument_get_arg_type(cmd->args, 3, &buf_len);
+  memset(&stats, 0, sizeof(stats));
+  buf = silc_argument_get_arg_type(args, 3, &buf_len);
+  if (buf) {
+    silc_buffer_set(&b, buf, buf_len);
+    silc_buffer_unformat(&b,
+                        SILC_STR_UI_INT(&stats.starttime),
+                        SILC_STR_UI_INT(&stats.uptime),
+                        SILC_STR_UI_INT(&stats.my_clients),
+                        SILC_STR_UI_INT(&stats.my_channels),
+                        SILC_STR_UI_INT(&stats.my_server_ops),
+                        SILC_STR_UI_INT(&stats.my_router_ops),
+                        SILC_STR_UI_INT(&stats.cell_clients),
+                        SILC_STR_UI_INT(&stats.cell_channels),
+                        SILC_STR_UI_INT(&stats.cell_servers),
+                        SILC_STR_UI_INT(&stats.clients),
+                        SILC_STR_UI_INT(&stats.channels),
+                        SILC_STR_UI_INT(&stats.servers),
+                        SILC_STR_UI_INT(&stats.routers),
+                        SILC_STR_UI_INT(&stats.server_ops),
+                        SILC_STR_UI_INT(&stats.router_ops),
+                        SILC_STR_END);
+  }
 
   /* Notify application */
-  COMMAND_REPLY((SILC_ARGS, buf, buf_len));
+  silc_client_command_callback(cmd, &stats);
 
  out:
-  SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_STATS);
-  silc_client_command_reply_free(cmd);
+  silc_fsm_next(fsm, silc_client_command_reply_process);
+  return SILC_FSM_CONTINUE;
 }
 
-/* Received reply to PING command. The reply time is shown to user. */
-
-SILC_CLIENT_CMD_REPLY_FUNC(ping)
-{
-  SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
-  SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
-  void *id;
-  int i;
-  time_t diff, curtime;
-
-  if (cmd->error != SILC_STATUS_OK) {
-    SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_ERROR,
-       "%s", silc_get_status_message(cmd->error));
-    COMMAND_REPLY_ERROR(cmd->error);
-    goto out;
-  }
+/********************************** PING ************************************/
 
-  curtime = time(NULL);
-  id = silc_id_str2id(cmd->packet->src_id, cmd->packet->src_id_len,
-                     cmd->packet->src_id_type);
-  if (!id || !conn->internal->ping) {
-    COMMAND_REPLY_ERROR(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
-    goto out;
-  }
+/* Received reply to PING command. */
 
-  for (i = 0; i < conn->internal->ping_count; i++) {
-    if (!conn->internal->ping[i].dest_id)
-      continue;
-    if (SILC_ID_SERVER_COMPARE(conn->internal->ping[i].dest_id, id)) {
-      diff = curtime - conn->internal->ping[i].start_time;
-      SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_INFO,
-         "Ping reply from %s: %d second%s",
-         conn->internal->ping[i].dest_name, diff,
-         diff == 1 ? "" : "s");
-
-      conn->internal->ping[i].start_time = 0;
-      silc_free(conn->internal->ping[i].dest_id);
-      conn->internal->ping[i].dest_id = NULL;
-      silc_free(conn->internal->ping[i].dest_name);
-      conn->internal->ping[i].dest_name = NULL;
-      break;
-    }
-  }
+SILC_FSM_STATE(silc_client_command_reply_ping)
+{
+  SilcClientCommandContext cmd = fsm_context;
+  SilcClientConnection conn = cmd->conn;
+  SilcClient client = conn->client;
+  SilcInt64 diff;
 
-  silc_free(id);
+  diff = silc_time() - SILC_PTR_TO_64(cmd->context);
+  SAY(client, conn, SILC_CLIENT_MESSAGE_INFO,
+      "Ping reply from %s: %d second%s", conn->remote_host,
+      (int)diff, diff == 1 ? "" : "s");
 
   /* Notify application */
-  COMMAND_REPLY((SILC_ARGS));
+  silc_client_command_callback(cmd);
 
- out:
-  SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_PING);
-  silc_client_command_reply_free(cmd);
+  silc_fsm_next(fsm, silc_client_command_reply_process);
+  return SILC_FSM_CONTINUE;
 }
 
+/********************************** JOIN ************************************/
+
 /* Received reply for JOIN command. */
 
-SILC_CLIENT_CMD_REPLY_FUNC(join)
+SILC_FSM_STATE(silc_client_command_reply_join)
 {
-  SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
-  SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
+  SilcClientCommandContext cmd = fsm_context;
+  SilcClientConnection conn = cmd->conn;
+  SilcClient client = conn->client;
+  SilcCommandPayload payload = state_context;
+  SilcArgumentPayload args = silc_command_get_args(payload);
   SilcChannelEntry channel;
   SilcChannelUser chu;
-  SilcChannelID *channel_id;
-  SilcUInt32 argc, mode = 0, len, list_count;
+  SilcUInt32 mode = 0, len, list_count;
   char *topic, *tmp, *channel_name = NULL, *hmac;
   SilcBuffer keyp = NULL, client_id_list = NULL, client_mode_list = NULL;
   SilcBufferStruct chpklist;
+  SilcID id;
   int i;
 
-  SILC_LOG_DEBUG(("Start"));
-
-  if (cmd->error != SILC_STATUS_OK) {
-    if (cmd->error != SILC_STATUS_ERR_USER_ON_CHANNEL)
-      SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_ERROR,
-         "Cannot join channel: %s", silc_get_status_message(cmd->error));
-    COMMAND_REPLY_ERROR(cmd->error);
-    goto out;
-  }
-
-  argc = silc_argument_get_arg_num(cmd->args);
-  if (argc < 7) {
-    SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_ERROR,
-       "Cannot join channel: Bad reply packet");
-    COMMAND_REPLY_ERROR(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
-    goto out;
-  }
+  /* Sanity checks */
+  CHECK_STATUS("Cannot join channel: ");
+  CHECK_ARGS(9, 17);
 
   /* Get channel name */
-  tmp = silc_argument_get_arg_type(cmd->args, 2, NULL);
-  if (!tmp) {
-    SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_ERROR,
-       "Cannot join channel: Bad reply packet");
-    COMMAND_REPLY_ERROR(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
+  channel_name = silc_argument_get_arg_type(args, 2, NULL);
+  if (!channel_name) {
+    ERROR_CALLBACK(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
     goto out;
   }
-  channel_name = tmp;
 
   /* Get Channel ID */
-  tmp = silc_argument_get_arg_type(cmd->args, 3, &len);
-  if (!tmp) {
-    SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_ERROR,
-       "Cannot join channel: Bad reply packet");
-    COMMAND_REPLY_ERROR(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
-    goto out;
-  }
-  channel_id = silc_id_payload_parse_id(tmp, len, NULL);
-  if (!channel_id) {
-    COMMAND_REPLY_ERROR(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
+  if (!silc_argument_get_decoded(args, 3, SILC_ARGUMENT_ID, &id, NULL)) {
+    ERROR_CALLBACK(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
     goto out;
   }
 
   /* Get channel mode */
-  tmp = silc_argument_get_arg_type(cmd->args, 5, NULL);
+  tmp = silc_argument_get_arg_type(args, 5, NULL);
   if (tmp)
     SILC_GET32_MSB(mode, tmp);
 
   /* Get channel key */
-  tmp = silc_argument_get_arg_type(cmd->args, 7, &len);
+  tmp = silc_argument_get_arg_type(args, 7, &len);
   if (tmp) {
-    keyp = silc_buffer_alloc(len);
-    silc_buffer_pull_tail(keyp, SILC_BUFFER_END(keyp));
-    silc_buffer_put(keyp, tmp, len);
+    keyp = silc_buffer_alloc_size(len);
+    if (keyp)
+      silc_buffer_put(keyp, tmp, len);
   }
 
   /* Get topic */
-  topic = silc_argument_get_arg_type(cmd->args, 10, NULL);
+  topic = silc_argument_get_arg_type(args, 10, NULL);
 
   /* Check whether we have this channel entry already. */
-  channel = silc_client_get_channel(cmd->client, conn, channel_name);
+  channel = silc_client_get_channel(client, conn, channel_name);
   if (channel) {
-    if (!SILC_ID_CHANNEL_COMPARE(channel->id, channel_id))
-      silc_client_replace_channel_id(cmd->client, conn, channel, channel_id);
+    if (!SILC_ID_CHANNEL_COMPARE(channel->id, &id.u.channel_id))
+      silc_client_replace_channel_id(client, conn, channel, &id.u.channel_id);
   } else {
     /* Create new channel entry */
-    channel = silc_client_add_channel(cmd->client, conn, channel_name,
-                                     mode, channel_id);
-  }
-  if (!channel) {
-    COMMAND_REPLY_ERROR(SILC_STATUS_ERR_BAD_CHANNEL);
-    goto out;
+    channel = silc_client_add_channel(client, conn, channel_name,
+                                     mode, &id.u.channel_id);
+    if (!channel) {
+      ERROR_CALLBACK(SILC_STATUS_ERR_BAD_CHANNEL);
+      goto out;
+    }
   }
 
   conn->current_channel = channel;
   channel->mode = mode;
 
   /* Get hmac */
-  hmac = silc_argument_get_arg_type(cmd->args, 11, NULL);
+  hmac = silc_argument_get_arg_type(args, 11, NULL);
   if (hmac) {
     if (!silc_hmac_alloc(hmac, NULL, &channel->hmac)) {
-      SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_ERROR,
-         "Cannot join channel: Unsupported HMAC `%s'", hmac);
-      COMMAND_REPLY_ERROR(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
+      if (cmd->verbose)
+       SAY(client, conn, SILC_CLIENT_MESSAGE_ERROR,
+           "Cannot join channel: Unsupported HMAC `%s'", hmac);
+      ERROR_CALLBACK(SILC_STATUS_ERR_UNKNOWN_ALGORITHM);
       goto out;
     }
   }
 
   /* Get the list count */
-  tmp = silc_argument_get_arg_type(cmd->args, 12, &len);
-  if (!tmp)
+  tmp = silc_argument_get_arg_type(args, 12, &len);
+  if (!tmp) {
+    ERROR_CALLBACK(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
     goto out;
+  }
   SILC_GET32_MSB(list_count, tmp);
 
   /* Get Client ID list */
-  tmp = silc_argument_get_arg_type(cmd->args, 13, &len);
-  if (!tmp)
+  tmp = silc_argument_get_arg_type(args, 13, &len);
+  if (!tmp) {
+    ERROR_CALLBACK(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
     goto out;
+  }
 
-  client_id_list = silc_buffer_alloc(len);
-  silc_buffer_pull_tail(client_id_list, len);
-  silc_buffer_put(client_id_list, tmp, len);
+  client_id_list = silc_buffer_alloc_size(len);
+  if (client_id_list)
+    silc_buffer_put(client_id_list, tmp, len);
 
   /* Get client mode list */
-  tmp = silc_argument_get_arg_type(cmd->args, 14, &len);
-  if (!tmp)
+  tmp = silc_argument_get_arg_type(args, 14, &len);
+  if (!tmp) {
+    ERROR_CALLBACK(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
     goto out;
+  }
 
-  client_mode_list = silc_buffer_alloc(len);
-  silc_buffer_pull_tail(client_mode_list, len);
-  silc_buffer_put(client_mode_list, tmp, len);
+  client_mode_list = silc_buffer_alloc_size(len);
+  if (client_mode_list)
+    silc_buffer_put(client_mode_list, tmp, len);
 
   /* Add clients we received in the reply to the channel */
   for (i = 0; i < list_count; i++) {
     SilcUInt16 idp_len;
     SilcUInt32 mode;
-    SilcClientID *client_id;
+    SilcID id;
     SilcClientEntry client_entry;
 
     /* Client ID */
     SILC_GET16_MSB(idp_len, client_id_list->data + 2);
     idp_len += 4;
-    client_id = silc_id_payload_parse_id(client_id_list->data, idp_len, NULL);
-    if (!client_id)
-      continue;
+    if (!silc_id_payload_parse_id(client_id_list->data, idp_len, &id))
+      goto out;
 
     /* Mode */
     SILC_GET32_MSB(mode, client_mode_list->data);
 
     /* Check if we have this client cached already. */
-    client_entry = silc_client_get_client_by_id(cmd->client, conn, client_id);
+    client_entry = silc_client_get_client_by_id(client, conn, &id.u.client_id);
     if (!client_entry) {
       /* No, we don't have it, add entry for it. */
       client_entry =
-       silc_client_add_client(cmd->client, conn, NULL, NULL, NULL,
-                              silc_id_dup(client_id, SILC_ID_CLIENT), 0);
+       silc_client_add_client(client, conn, NULL, NULL, NULL,
+                              &id.u.client_id, 0);
       if (!client_entry)
        goto out;
     }
@@ -1139,6 +1153,8 @@ SILC_CLIENT_CMD_REPLY_FUNC(join)
     /* Join client to the channel */
     if (!silc_client_on_channel(channel, client_entry)) {
       chu = silc_calloc(1, sizeof(*chu));
+      if (!chu)
+       goto out;
       chu->client = client_entry;
       chu->channel = channel;
       chu->mode = mode;
@@ -1146,37 +1162,36 @@ SILC_CLIENT_CMD_REPLY_FUNC(join)
       silc_hash_table_add(client_entry->channels, channel, chu);
     }
 
-    silc_free(client_id);
     silc_buffer_pull(client_id_list, idp_len);
     silc_buffer_pull(client_mode_list, 4);
   }
-  silc_buffer_push(client_id_list, client_id_list->data -
-                  client_id_list->head);
-  silc_buffer_push(client_mode_list, client_mode_list->data -
-                  client_mode_list->head);
+  silc_buffer_start(client_id_list);
+  silc_buffer_start(client_mode_list);
 
   /* Save channel key */
+#if 0
   if (keyp)
-    silc_client_save_channel_key(cmd->client, conn, keyp, channel);
+    silc_client_save_channel_key(client, conn, keyp, channel);
+#endif /* 0 */
 
   /* Get founder key */
-  tmp = silc_argument_get_arg_type(cmd->args, 15, &len);
+  tmp = silc_argument_get_arg_type(args, 15, &len);
   if (tmp) {
     if (channel->founder_key)
       silc_pkcs_public_key_free(channel->founder_key);
     channel->founder_key = NULL;
-    silc_pkcs_public_key_payload_decode(tmp, len, &channel->founder_key);
+    silc_public_key_payload_decode(tmp, len, &channel->founder_key);
   }
 
   /* Get user limit */
-  tmp = silc_argument_get_arg_type(cmd->args, 17, &len);
+  tmp = silc_argument_get_arg_type(args, 17, &len);
   if (tmp && len == 4)
     SILC_GET32_MSB(channel->user_limit, tmp);
   if (!(channel->mode & SILC_CHANNEL_MODE_ULIMIT))
     channel->user_limit = 0;
 
   /* Get channel public key list */
-  tmp = silc_argument_get_arg_type(cmd->args, 16, &len);
+  tmp = silc_argument_get_arg_type(args, 16, &len);
   if (tmp)
     silc_buffer_set(&chpklist, tmp, len);
 
@@ -1186,46 +1201,42 @@ SILC_CLIENT_CMD_REPLY_FUNC(join)
   }
 
   /* Notify application */
-  COMMAND_REPLY((SILC_ARGS, channel_name, channel, mode, 0,
-                keyp ? keyp->head : NULL, NULL,
-                NULL, topic, hmac, list_count, client_id_list,
-                client_mode_list, channel->founder_key,
-                tmp ? &chpklist : NULL, channel->user_limit));
+  silc_client_command_callback(cmd, channel_name, channel, mode, 0,
+                              keyp ? keyp->head : NULL, NULL,
+                              NULL, topic, hmac, list_count, client_id_list,
+                              client_mode_list, channel->founder_key,
+                              tmp ? &chpklist : NULL, channel->user_limit);
 
  out:
-  SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_JOIN);
-  silc_client_command_reply_free(cmd);
   silc_buffer_free(keyp);
   silc_buffer_free(client_id_list);
   silc_buffer_free(client_mode_list);
+  silc_fsm_next(fsm, silc_client_command_reply_process);
+  return SILC_FSM_CONTINUE;
 }
 
+/********************************** MOTD ************************************/
+
 /* Received reply for MOTD command */
 
-SILC_CLIENT_CMD_REPLY_FUNC(motd)
+SILC_FSM_STATE(silc_client_command_reply_motd)
 {
-  SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
-  SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
-  SilcUInt32 argc, i;
+  SilcClientCommandContext cmd = fsm_context;
+  SilcClientConnection conn = cmd->conn;
+  SilcClient client = conn->client;
+  SilcCommandPayload payload = state_context;
+  SilcArgumentPayload args = silc_command_get_args(payload);
+  SilcUInt32 i;
   char *motd = NULL, *cp, line[256];
 
-  if (cmd->error != SILC_STATUS_OK) {
-    SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_ERROR,
-       "%s", silc_get_status_message(cmd->error));
-    COMMAND_REPLY_ERROR(cmd->error);
-    return;
-  }
+  /* Sanity checks */
+  CHECK_STATUS("Cannot get motd: ");
+  CHECK_ARGS(2, 3);
 
-  argc = silc_argument_get_arg_num(cmd->args);
-  if (argc > 3) {
-    COMMAND_REPLY_ERROR(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
-    goto out;
-  }
-
-  if (argc == 3) {
-    motd = silc_argument_get_arg_type(cmd->args, 3, NULL);
+  if (silc_argument_get_arg_num(args) == 3) {
+    motd = silc_argument_get_arg_type(args, 3, NULL);
     if (!motd) {
-      COMMAND_REPLY_ERROR(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
+      ERROR_CALLBACK(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
       goto out;
     }
 
@@ -1240,7 +1251,8 @@ SILC_CLIENT_CMD_REPLY_FUNC(motd)
        if (i == 2)
          line[0] = ' ';
 
-       SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_INFO, "%s", line);
+       if (cmd->verbose)
+         SAY(client, conn, SILC_CLIENT_MESSAGE_INFO, "%s", line);
 
        if (!strlen(cp))
          break;
@@ -1250,32 +1262,33 @@ SILC_CLIENT_CMD_REPLY_FUNC(motd)
   }
 
   /* Notify application */
-  COMMAND_REPLY((SILC_ARGS, motd));
+  silc_client_command_callback(cmd, motd);
 
  out:
-  SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_MOTD);
-  silc_client_command_reply_free(cmd);
+  silc_fsm_next(fsm, silc_client_command_reply_process);
+  return SILC_FSM_CONTINUE;
 }
 
+/********************************** UMODE ***********************************/
+
 /* Received reply tot he UMODE command. Save the current user mode */
 
-SILC_CLIENT_CMD_REPLY_FUNC(umode)
+SILC_FSM_STATE(silc_client_command_reply_umode)
 {
-  SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
-  SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
+  SilcClientCommandContext cmd = fsm_context;
+  SilcClientConnection conn = cmd->conn;
+  SilcCommandPayload payload = state_context;
+  SilcArgumentPayload args = silc_command_get_args(payload);
   unsigned char *tmp;
-  SilcUInt32 mode;
+  SilcUInt32 mode, len;
 
-  if (cmd->error != SILC_STATUS_OK) {
-    SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_ERROR,
-       "Cannot change mode: %s", silc_get_status_message(cmd->error));
-    COMMAND_REPLY_ERROR(cmd->error);
-    goto out;
-  }
+  /* Sanity checks */
+  CHECK_STATUS("Cannot change mode: ");
+  CHECK_ARGS(2, 2);
 
-  tmp = silc_argument_get_arg_type(cmd->args, 2, NULL);
-  if (!tmp) {
-    COMMAND_REPLY_ERROR(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
+  tmp = silc_argument_get_arg_type(args, 2, &len);
+  if (!tmp || len != 4) {
+    ERROR_CALLBACK(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
     goto out;
   }
 
@@ -1283,55 +1296,53 @@ SILC_CLIENT_CMD_REPLY_FUNC(umode)
   conn->local_entry->mode = mode;
 
   /* Notify application */
-  COMMAND_REPLY((SILC_ARGS, mode));
+  silc_client_command_callback(cmd, mode);
 
  out:
-  SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_UMODE);
-  silc_client_command_reply_free(cmd);
+  silc_fsm_next(fsm, silc_client_command_reply_process);
+  return SILC_FSM_CONTINUE;
 }
 
+/********************************** CMODE ***********************************/
+
 /* Received reply for CMODE command. */
 
-SILC_CLIENT_CMD_REPLY_FUNC(cmode)
+SILC_FSM_STATE(silc_client_command_reply_cmode)
 {
-  SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
-  SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
+  SilcClientCommandContext cmd = fsm_context;
+  SilcClientConnection conn = cmd->conn;
+  SilcClient client = conn->client;
+  SilcCommandPayload payload = state_context;
+  SilcArgumentPayload args = silc_command_get_args(payload);
   unsigned char *tmp;
   SilcUInt32 mode;
-  SilcChannelID *channel_id;
   SilcChannelEntry channel;
   SilcUInt32 len;
   SilcPublicKey public_key = NULL;
   SilcBufferStruct channel_pubkeys;
+  SilcID id;
 
-  if (cmd->error != SILC_STATUS_OK) {
-    SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_ERROR,
-       "Cannot change mode: %s", silc_get_status_message(cmd->error));
-    COMMAND_REPLY_ERROR(cmd->error);
-    goto out;
-  }
+  /* Sanity checks */
+  CHECK_STATUS("Cannot change mode: ");
+  CHECK_ARGS(3, 6);
 
   /* Take Channel ID */
-  tmp = silc_argument_get_arg_type(cmd->args, 2, &len);
-  if (!tmp)
-    goto out;
-  channel_id = silc_id_payload_parse_id(tmp, len, NULL);
-  if (!channel_id)
+  if (!silc_argument_get_decoded(args, 2, SILC_ARGUMENT_ID, &id, NULL)) {
+    ERROR_CALLBACK(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
     goto out;
+  }
 
   /* Get the channel entry */
-  channel = silc_client_get_channel_by_id(cmd->client, conn, channel_id);
+  channel = silc_client_get_channel_by_id(client, conn, &id.u.channel_id);
   if (!channel) {
-    silc_free(channel_id);
-    COMMAND_REPLY_ERROR(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
+    ERROR_CALLBACK(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
     goto out;
   }
 
   /* Get channel mode */
-  tmp = silc_argument_get_arg_type(cmd->args, 3, NULL);
-  if (!tmp) {
-    silc_free(channel_id);
-    COMMAND_REPLY_ERROR(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
+  tmp = silc_argument_get_arg_type(args, 3, &len);
+  if (!tmp || len != 4) {
+    ERROR_CALLBACK(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
     goto out;
   }
 
@@ -1340,1000 +1351,602 @@ SILC_CLIENT_CMD_REPLY_FUNC(cmode)
   channel->mode = mode;
 
   /* Get founder public key */
-  tmp = silc_argument_get_arg_type(cmd->args, 4, &len);
-  if (tmp) {
-    if (!silc_pkcs_public_key_payload_decode(tmp, len, &public_key))
-      public_key = NULL;
-  }
+  tmp = silc_argument_get_arg_type(args, 4, &len);
+  if (tmp)
+    silc_public_key_payload_decode(tmp, len, &public_key);
 
   /* Get user limit */
-  tmp = silc_argument_get_arg_type(cmd->args, 6, &len);
+  tmp = silc_argument_get_arg_type(args, 6, &len);
   if (tmp && len == 4)
     SILC_GET32_MSB(channel->user_limit, tmp);
   if (!(channel->mode & SILC_CHANNEL_MODE_ULIMIT))
     channel->user_limit = 0;
 
   /* Get channel public key(s) */
-  tmp = silc_argument_get_arg_type(cmd->args, 5, &len);
+  tmp = silc_argument_get_arg_type(args, 5, &len);
   if (tmp)
     silc_buffer_set(&channel_pubkeys, tmp, len);
 
   /* Notify application */
-  COMMAND_REPLY((SILC_ARGS, channel, mode, public_key,
-                tmp ? &channel_pubkeys : NULL, channel->user_limit));
-
-  silc_free(channel_id);
+  silc_client_command_callback(cmd, channel, mode, public_key,
+                              tmp ? &channel_pubkeys : NULL,
+                              channel->user_limit);
 
  out:
   if (public_key)
     silc_pkcs_public_key_free(public_key);
-  SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_CMODE);
-  silc_client_command_reply_free(cmd);
+  silc_fsm_next(fsm, silc_client_command_reply_process);
+  return SILC_FSM_CONTINUE;
 }
 
+/********************************** CUMODE **********************************/
+
 /* Received reply for CUMODE command */
 
-SILC_CLIENT_CMD_REPLY_FUNC(cumode)
+SILC_FSM_STATE(silc_client_command_reply_cumode)
 {
-  SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
-  SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
-  SilcClientID *client_id;
-  SilcChannelID *channel_id;
+  SilcClientCommandContext cmd = fsm_context;
+  SilcClientConnection conn = cmd->conn;
+  SilcClient client = conn->client;
+  SilcCommandPayload payload = state_context;
+  SilcArgumentPayload args = silc_command_get_args(payload);
   SilcClientEntry client_entry;
   SilcChannelEntry channel;
   SilcChannelUser chu;
-  unsigned char *modev, *tmp, *id;
+  unsigned char *modev;
   SilcUInt32 len, mode;
+  SilcID id;
 
-  if (cmd->error != SILC_STATUS_OK) {
-    SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_ERROR,
-       "Cannot change mode: %s", silc_get_status_message(cmd->error));
-    COMMAND_REPLY_ERROR(cmd->error);
-    goto out;
-  }
+  /* Sanity checks */
+  CHECK_STATUS("Cannot change mode: ");
+  CHECK_ARGS(4, 4);
 
   /* Get channel mode */
-  modev = silc_argument_get_arg_type(cmd->args, 2, NULL);
-  if (!modev) {
-    COMMAND_REPLY_ERROR(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
+  modev = silc_argument_get_arg_type(args, 2, &len);
+  if (!modev || len != 4) {
+    ERROR_CALLBACK(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
     goto out;
   }
+  SILC_GET32_MSB(mode, modev);
 
   /* Take Channel ID */
-  tmp = silc_argument_get_arg_type(cmd->args, 3, &len);
-  if (!tmp)
-    goto out;
-  channel_id = silc_id_payload_parse_id(tmp, len, NULL);
-  if (!channel_id)
+  if (!silc_argument_get_decoded(args, 3, SILC_ARGUMENT_ID, &id, NULL)) {
+    ERROR_CALLBACK(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
     goto out;
+  }
 
   /* Get the channel entry */
-  channel = silc_client_get_channel_by_id(cmd->client, conn, channel_id);
+  channel = silc_client_get_channel_by_id(client, conn, &id.u.channel_id);
   if (!channel) {
-    silc_free(channel_id);
-    COMMAND_REPLY_ERROR(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
+    ERROR_CALLBACK(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
     goto out;
   }
 
   /* Get Client ID */
-  id = silc_argument_get_arg_type(cmd->args, 4, &len);
-  if (!id) {
-    silc_free(channel_id);
-    COMMAND_REPLY_ERROR(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
-    goto out;
-  }
-  client_id = silc_id_payload_parse_id(id, len, NULL);
-  if (!client_id) {
-    silc_free(channel_id);
-    COMMAND_REPLY_ERROR(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
+  if (!silc_argument_get_decoded(args, 4, SILC_ARGUMENT_ID, &id, NULL)) {
+    ERROR_CALLBACK(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
     goto out;
   }
 
   /* Get client entry */
-  client_entry = silc_client_get_client_by_id(cmd->client, conn, client_id);
+  client_entry = silc_client_get_client_by_id(client, conn, &id.u.client_id);
   if (!client_entry) {
-    silc_free(channel_id);
-    silc_free(client_id);
-    COMMAND_REPLY_ERROR(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
+    ERROR_CALLBACK(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
     goto out;
   }
 
   /* Save the mode */
-  SILC_GET32_MSB(mode, modev);
   chu = silc_client_on_channel(channel, client_entry);
   if (chu)
     chu->mode = mode;
 
   /* Notify application */
-  COMMAND_REPLY((SILC_ARGS, mode, channel, client_entry));
-  silc_free(client_id);
-  silc_free(channel_id);
+  silc_client_command_callback(cmd, mode, channel, client_entry);
 
  out:
-  SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_CUMODE);
-  silc_client_command_reply_free(cmd);
+  silc_fsm_next(fsm, silc_client_command_reply_process);
+  return SILC_FSM_CONTINUE;
 }
 
-SILC_CLIENT_CMD_REPLY_FUNC(kick)
+/********************************** KICK ************************************/
+
+SILC_FSM_STATE(silc_client_command_reply_kick)
 {
-  SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
-  SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
-  SilcClientID *client_id = NULL;
-  SilcChannelID *channel_id = NULL;
-  SilcClientEntry client_entry = NULL;
-  SilcChannelEntry channel = NULL;
-  unsigned char *tmp;
-  SilcUInt32 len;
+  SilcClientCommandContext cmd = fsm_context;
+  SilcClientConnection conn = cmd->conn;
+  SilcClient client = conn->client;
+  SilcCommandPayload payload = state_context;
+  SilcArgumentPayload args = silc_command_get_args(payload);
+  SilcClientEntry client_entry;
+  SilcChannelEntry channel;
+  SilcID id;
 
-  if (cmd->error != SILC_STATUS_OK) {
-    SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_ERROR,
-       "Cannot kick: %s", silc_get_status_message(cmd->error));
-    COMMAND_REPLY_ERROR(cmd->error);
-    goto out;
-  }
+  /* Sanity checks */
+  CHECK_STATUS("Cannot kick: ");
+  CHECK_ARGS(3, 3);
 
   /* Take Channel ID */
-  tmp = silc_argument_get_arg_type(cmd->args, 2, &len);
-  if (tmp) {
-    channel_id = silc_id_payload_parse_id(tmp, len, NULL);
-    if (!channel_id) {
-      COMMAND_REPLY_ERROR(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
-      goto out;
-    }
+  if (!silc_argument_get_decoded(args, 2, SILC_ARGUMENT_ID, &id, NULL)) {
+    ERROR_CALLBACK(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
+    goto out;
+  }
 
-    /* Get the channel entry */
-    channel = silc_client_get_channel_by_id(cmd->client, conn, channel_id);
-    if (!channel) {
-      COMMAND_REPLY_ERROR(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
-      goto out;
-    }
+  /* Get the channel entry */
+  channel = silc_client_get_channel_by_id(client, conn, &id.u.channel_id);
+  if (!channel) {
+    ERROR_CALLBACK(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
+    goto out;
   }
 
   /* Get Client ID */
-  tmp = silc_argument_get_arg_type(cmd->args, 3, &len);
-  if (tmp) {
-    client_id = silc_id_payload_parse_id(tmp, len, NULL);
-    if (!client_id) {
-      COMMAND_REPLY_ERROR(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
-      goto out;
-    }
+  if (!silc_argument_get_decoded(args, 3, SILC_ARGUMENT_ID, &id, NULL)) {
+    ERROR_CALLBACK(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
+    goto out;
+  }
 
-    /* Get client entry */
-    client_entry = silc_client_get_client_by_id(cmd->client, conn, client_id);
-    if (!client_entry) {
-      COMMAND_REPLY_ERROR(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
-      goto out;
-    }
+  /* Get client entry */
+  client_entry = silc_client_get_client_by_id(client, conn, &id.u.client_id);
+  if (!client_entry) {
+    ERROR_CALLBACK(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
+    goto out;
   }
 
   /* Notify application */
-  COMMAND_REPLY((SILC_ARGS, channel, client_entry));
+  silc_client_command_callback(cmd, channel, client_entry);
 
  out:
-  silc_free(channel_id);
-  silc_free(client_id);
-  SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_KICK);
-  silc_client_command_reply_free(cmd);
+  silc_fsm_next(fsm, silc_client_command_reply_process);
+  return SILC_FSM_CONTINUE;
 }
 
-SILC_CLIENT_CMD_REPLY_FUNC(silcoper)
+/******************************** SILCOPER **********************************/
+
+SILC_FSM_STATE(silc_client_command_reply_silcoper)
 {
-  SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
-  SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
+  SilcClientCommandContext cmd = fsm_context;
+  SilcCommandPayload payload = state_context;
+  SilcArgumentPayload args = silc_command_get_args(payload);
 
-  if (cmd->error != SILC_STATUS_OK) {
-    SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_ERROR,
-       "%s", silc_get_status_message(cmd->error));
-    COMMAND_REPLY_ERROR(cmd->error);
-    goto out;
-  }
+  /* Sanity checks */
+  CHECK_STATUS("Cannot change mode: ");
+  CHECK_ARGS(1, 1);
 
   /* Notify application */
-  COMMAND_REPLY((SILC_ARGS));
+  silc_client_command_callback(cmd);
 
- out:
-  SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_SILCOPER);
-  silc_client_command_reply_free(cmd);
+  silc_fsm_next(fsm, silc_client_command_reply_process);
+  return SILC_FSM_CONTINUE;
 }
 
-SILC_CLIENT_CMD_REPLY_FUNC(oper)
+/********************************** OPER ************************************/
+
+SILC_FSM_STATE(silc_client_command_reply_oper)
 {
-  SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
-  SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
+  SilcClientCommandContext cmd = fsm_context;
+  SilcCommandPayload payload = state_context;
+  SilcArgumentPayload args = silc_command_get_args(payload);
 
-  if (cmd->error != SILC_STATUS_OK) {
-    SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_ERROR,
-       "%s", silc_get_status_message(cmd->error));
-    COMMAND_REPLY_ERROR(cmd->error);
-    goto out;
-  }
+  /* Sanity checks */
+  CHECK_STATUS("Cannot change mode: ");
+  CHECK_ARGS(1, 1);
 
   /* Notify application */
-  COMMAND_REPLY((SILC_ARGS));
+  silc_client_command_callback(cmd);
 
- out:
-  SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_OPER);
-  silc_client_command_reply_free(cmd);
+  silc_fsm_next(fsm, silc_client_command_reply_process);
+  return SILC_FSM_CONTINUE;
 }
 
-SILC_CLIENT_CMD_REPLY_FUNC(detach)
+/********************************* DETACH ***********************************/
+
+SILC_FSM_STATE(silc_client_command_reply_detach)
 {
-  SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
-  SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
+  SilcClientCommandContext cmd = fsm_context;
+  SilcClientConnection conn = cmd->conn;
+  SilcClient client = conn->client;
+  SilcCommandPayload payload = state_context;
+  SilcArgumentPayload args = silc_command_get_args(payload);
   SilcBuffer detach;
 
-  if (cmd->error != SILC_STATUS_OK) {
-    SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_ERROR,
-       "%s", silc_get_status_message(cmd->error));
-    COMMAND_REPLY_ERROR(cmd->error);
-    goto out;
-  }
+  /* Sanity checks */
+  CHECK_STATUS("Cannot detach: ");
+  CHECK_ARGS(1, 1);
 
   /* Notify application */
-  COMMAND_REPLY((SILC_ARGS));
+  silc_client_command_callback(cmd);
 
+#if 0
   /* Generate the detachment data and deliver it to the client in the
      detach client operation */
-  detach = silc_client_get_detach_data(cmd->client, conn);
+  detach = silc_client_get_detach_data(client, conn);
   if (detach) {
-    cmd->client->internal->ops->detach(cmd->client, conn,
-                                      detach->data, detach->len);
+    client->internal->ops->detach(client, conn, silc_buffer_data(detach),
+                                 silc_buffer_len(detach));
     silc_buffer_free(detach);
   }
+#endif /* 0 */
 
- out:
-  SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_DETACH);
-  silc_client_command_reply_free(cmd);
+  silc_fsm_next(fsm, silc_client_command_reply_process);
+  return SILC_FSM_CONTINUE;
 }
 
-SILC_CLIENT_CMD_REPLY_FUNC(watch)
+/********************************** WATCH ***********************************/
+
+SILC_FSM_STATE(silc_client_command_reply_watch)
 {
-  SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
-  SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
+  SilcClientCommandContext cmd = fsm_context;
+  SilcCommandPayload payload = state_context;
+  SilcArgumentPayload args = silc_command_get_args(payload);
 
-  if (cmd->error != SILC_STATUS_OK) {
-    SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_ERROR,
-       "%s", silc_get_status_message(cmd->error));
-    COMMAND_REPLY_ERROR(cmd->error);
-    goto out;
-  }
+  /* Sanity checks */
+  CHECK_STATUS("Cannot set watch: ");
+  CHECK_ARGS(1, 1);
 
   /* Notify application */
-  COMMAND_REPLY((SILC_ARGS));
+  silc_client_command_callback(cmd);
 
- out:
-  SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_WATCH);
-  silc_client_command_reply_free(cmd);
+  silc_fsm_next(fsm, silc_client_command_reply_process);
+  return SILC_FSM_CONTINUE;
 }
 
-SILC_CLIENT_CMD_REPLY_FUNC(ban)
+/*********************************** BAN ************************************/
+
+SILC_FSM_STATE(silc_client_command_reply_ban)
 {
-  SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
-  SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
+  SilcClientCommandContext cmd = fsm_context;
+  SilcClientConnection conn = cmd->conn;
+  SilcClient client = conn->client;
+  SilcCommandPayload payload = state_context;
+  SilcArgumentPayload args = silc_command_get_args(payload);
   SilcChannelEntry channel;
-  SilcChannelID *channel_id;
   unsigned char *tmp;
   SilcUInt32 len;
   SilcBufferStruct buf;
+  SilcID id;
 
-  if (cmd->error != SILC_STATUS_OK) {
-    SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_ERROR,
-       "%s", silc_get_status_message(cmd->error));
-    COMMAND_REPLY_ERROR(cmd->error);
-    goto out;
-  }
+  /* Sanity checks */
+  CHECK_STATUS("Cannot set ban: ");
+  CHECK_ARGS(2, 3);
 
   /* Take Channel ID */
-  tmp = silc_argument_get_arg_type(cmd->args, 2, &len);
-  if (!tmp)
-    goto out;
-
-  channel_id = silc_id_payload_parse_id(tmp, len, NULL);
-  if (!channel_id)
+  if (!silc_argument_get_decoded(args, 2, SILC_ARGUMENT_ID, &id, NULL)) {
+    ERROR_CALLBACK(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
     goto out;
+  }
 
   /* Get the channel entry */
-  channel = silc_client_get_channel_by_id(cmd->client, conn, channel_id);
+  channel = silc_client_get_channel_by_id(client, conn, &id.u.channel_id);
   if (!channel) {
-    silc_free(channel_id);
-    COMMAND_REPLY_ERROR(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
+    ERROR_CALLBACK(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
     goto out;
   }
 
   /* Get the ban list */
-  tmp = silc_argument_get_arg_type(cmd->args, 3, &len);
+  tmp = silc_argument_get_arg_type(args, 3, &len);
   if (tmp)
     silc_buffer_set(&buf, tmp, len);
 
   /* Notify application */
-  COMMAND_REPLY((SILC_ARGS, channel, tmp ? &buf : NULL));
+  silc_client_command_callback(cmd, channel, tmp ? &buf : NULL);
 
  out:
-  SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_BAN);
-  silc_client_command_reply_free(cmd);
+  silc_fsm_next(fsm, silc_client_command_reply_process);
+  return SILC_FSM_CONTINUE;
 }
 
+/********************************** LEAVE ***********************************/
+
 /* Reply to LEAVE command. */
 
-SILC_CLIENT_CMD_REPLY_FUNC(leave)
+SILC_FSM_STATE(silc_client_command_reply_leave)
 {
-  SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
-  SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
-  SilcChannelID *channel_id;
-  SilcChannelEntry channel = NULL;
+  SilcClientCommandContext cmd = fsm_context;
+  SilcClientConnection conn = cmd->conn;
+  SilcClient client = conn->client;
+  SilcCommandPayload payload = state_context;
+  SilcArgumentPayload args = silc_command_get_args(payload);
+  SilcChannelEntry channel;
   SilcChannelUser chu;
-  unsigned char *tmp;
-  SilcUInt32 len;
+  SilcID id;
 
-  if (cmd->error != SILC_STATUS_OK) {
-    SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_ERROR,
-       "%s", silc_get_status_message(cmd->error));
-    COMMAND_REPLY_ERROR(cmd->error);
+  /* Sanity checks */
+  CHECK_STATUS("Cannot set leave: ");
+  CHECK_ARGS(2, 2);
+
+  /* Get Channel ID */
+  if (!silc_argument_get_decoded(args, 2, SILC_ARGUMENT_ID, &id, NULL)) {
+    ERROR_CALLBACK(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
     goto out;
   }
 
-  /* From protocol version 1.1 we get the channel ID of the left channel */
-  tmp = silc_argument_get_arg_type(cmd->args, 2, &len);
-  if (tmp) {
-    channel_id = silc_id_payload_parse_id(tmp, len, NULL);
-    if (!channel_id)
-      goto out;
-
-    /* Get the channel entry */
-    channel = silc_client_get_channel_by_id(cmd->client, conn, channel_id);
-    if (!channel) {
-      silc_free(channel_id);
-      COMMAND_REPLY_ERROR(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
-      goto out;
-    }
-
-    /* Remove us from this channel. */
-    chu = silc_client_on_channel(channel, conn->local_entry);
-    if (chu) {
-      silc_hash_table_del(chu->client->channels, chu->channel);
-      silc_hash_table_del(chu->channel->user_list, chu->client);
-      silc_free(chu);
-    }
+  /* Get the channel entry */
+  channel = silc_client_get_channel_by_id(client, conn, &id.u.channel_id);
+  if (!channel) {
+    ERROR_CALLBACK(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
+    goto out;
+  }
 
-    silc_free(channel_id);
+  /* Remove us from this channel. */
+  chu = silc_client_on_channel(channel, conn->local_entry);
+  if (chu) {
+    silc_hash_table_del(chu->client->channels, chu->channel);
+    silc_hash_table_del(chu->channel->user_list, chu->client);
+    silc_free(chu);
   }
 
   /* Notify application */
-  COMMAND_REPLY((SILC_ARGS, channel));
+  silc_client_command_callback(cmd, channel);
 
   /* Now delete the channel. */
-  if (channel)
-    silc_client_del_channel(cmd->client, conn, channel);
+  silc_client_del_channel(client, conn, channel);
 
  out:
-  SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_LEAVE);
-  silc_client_command_reply_free(cmd);
+  silc_fsm_next(fsm, silc_client_command_reply_process);
+  return SILC_FSM_CONTINUE;
 }
 
-/* Channel resolving callback for USERS command reply. */
+/********************************* USERS ************************************/
 
-static void silc_client_command_reply_users_cb(SilcClient client,
-                                              SilcClientConnection conn,
-                                              SilcChannelEntry *channels,
-                                              SilcUInt32 channels_count,
-                                              void *context)
+static SilcBool
+silc_client_command_reply_users_continue(SilcClient client,
+                                        SilcClientConnection conn,
+                                        SilcCommand command,
+                                        SilcStatus status,
+                                        SilcStatus error,
+                                        void *context,
+                                        va_list ap)
 {
-  if (!channels_count) {
-    SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
-    SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
+  SilcClientCommandContext cmd = context;
 
-    cmd->status = cmd->error = SILC_STATUS_ERR_NO_SUCH_CHANNEL;
-    SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_ERROR,
-       "%s", silc_get_status_message(cmd->error));
-    COMMAND_REPLY_ERROR(cmd->error);
-    SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_USERS);
-    silc_client_command_reply_free(cmd);
-    return;
-  }
+  return TRUE;
+}
 
-  silc_client_command_reply_users(context, NULL);
+/* Continue USERS command after resolving unknown users */
+
+static void
+silc_client_command_reply_users_resolved(SilcClient client,
+                                        SilcClientConnection conn,
+                                        SilcStatus status,
+                                        SilcDList clients,
+                                        void *context)
+{
+  SilcClientCommandContext cmd = context;
+  SILC_FSM_CALL_CONTINUE_SYNC(&cmd->thread);
 }
 
-static int
-silc_client_command_reply_users_save(SilcClientCommandReplyContext cmd,
-                                    SilcStatus status,
-                                    SilcBool notify,
-                                    SilcBool resolve,
-                                    SilcGetChannelCallback get_channel,
-                                    SilcCommandCb get_clients)
+/* Reply to USERS command. Received list of client ID's and theirs modes
+   on the channel we requested. */
+
+SILC_FSM_STATE(silc_client_command_reply_users)
 {
-  SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
+  SilcClientCommandContext cmd = fsm_context;
+  SilcClientConnection conn = cmd->conn;
+  SilcClient client = conn->client;
+  SilcCommandPayload payload = state_context;
+  SilcArgumentPayload args = silc_command_get_args(payload);
+  unsigned char *tmp;
+  SilcUInt32 tmp_len, list_count;
+  SilcUInt16 idp_len, mode;
+  SilcHashTableList htl;
+  SilcBufferStruct client_id_list, client_mode_list;
   SilcChannelEntry channel;
   SilcClientEntry client_entry;
   SilcChannelUser chu;
-  SilcChannelID *channel_id = NULL;
-  SilcBufferStruct client_id_list, client_mode_list;
-  unsigned char *tmp;
-  SilcUInt32 tmp_len, list_count;
+  SilcID id;
   int i;
-  unsigned char **res_argv = NULL;
-  SilcUInt32 *res_argv_lens = NULL, *res_argv_types = NULL, res_argc = 0;
-  SilcBool wait_res = FALSE;
 
-  SILC_LOG_DEBUG(("Start"));
+  /* Sanity checks */
+  CHECK_STATUS("Cannot get users: ");
+  CHECK_ARGS(5, 5);
 
   /* Get channel ID */
-  tmp = silc_argument_get_arg_type(cmd->args, 2, &tmp_len);
-  if (!tmp) {
-    COMMAND_REPLY_ERROR(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
+  if (!silc_argument_get_decoded(args, 2, SILC_ARGUMENT_ID, &id, NULL)) {
+    ERROR_CALLBACK(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
     goto out;
   }
-  channel_id = silc_id_payload_parse_id(tmp, tmp_len, NULL);
-  if (!channel_id) {
-    COMMAND_REPLY_ERROR(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
-    goto out;
+
+  /* Get channel entry */
+  channel = silc_client_get_channel_by_id(client, conn, &id.u.channel_id);
+  if (!channel) {
+    /* Resolve the channel from server */
+#if 0
+    SILC_FSM_CALL(silc_client_get_channel_by_id_resolve(
+                       client, conn, &id.u.channel_id,
+                       silc_client_command_reply_users_continue, cmd));
+#endif /* 0 */
+    /* NOT REACHED */
   }
 
   /* Get the list count */
-  tmp = silc_argument_get_arg_type(cmd->args, 3, &tmp_len);
-  if (!tmp) {
-    COMMAND_REPLY_ERROR(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
+  tmp = silc_argument_get_arg_type(args, 3, &tmp_len);
+  if (!tmp || tmp_len != 4) {
+    ERROR_CALLBACK(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
     goto out;
   }
   SILC_GET32_MSB(list_count, tmp);
 
   /* Get Client ID list */
-  tmp = silc_argument_get_arg_type(cmd->args, 4, &tmp_len);
+  tmp = silc_argument_get_arg_type(args, 4, &tmp_len);
   if (!tmp) {
-    COMMAND_REPLY_ERROR(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
+    ERROR_CALLBACK(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
     goto out;
   }
   silc_buffer_set(&client_id_list, tmp, tmp_len);
 
+  /* Resolve users we do not know about */
+  if (!cmd->resolved) {
+    cmd->resolved = TRUE;
+    SILC_FSM_CALL(silc_client_get_clients_by_list(
+                         client, conn, list_count, &client_id_list,
+                         silc_client_command_reply_users_resolved, cmd));
+    /* NOT REACHED */
+  }
+
   /* Get client mode list */
-  tmp = silc_argument_get_arg_type(cmd->args, 5, &tmp_len);
+  tmp = silc_argument_get_arg_type(args, 5, &tmp_len);
   if (!tmp) {
-    COMMAND_REPLY_ERROR(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
+    ERROR_CALLBACK(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
     goto out;
   }
   silc_buffer_set(&client_mode_list, tmp, tmp_len);
 
-  /* Get channel entry */
-  channel = silc_client_get_channel_by_id(cmd->client, conn, channel_id);
-  if (!channel) {
-    /* Resolve the channel from server */
-    silc_client_get_channel_by_id_resolve(cmd->client, conn, channel_id,
-                                         get_channel, cmd);
-    silc_free(channel_id);
-    return 1;
-  }
-
   SILC_LOG_DEBUG(("channel %s, %d users", channel->channel_name, list_count));
 
   /* Cache the received Client ID's and modes. */
   for (i = 0; i < list_count; i++) {
-    SilcUInt16 idp_len;
-    SilcUInt32 mode;
-    SilcClientID *client_id;
-
-    /* Client ID */
     SILC_GET16_MSB(idp_len, client_id_list.data + 2);
     idp_len += 4;
-    client_id = silc_id_payload_parse_id(client_id_list.data, idp_len, NULL);
-    if (!client_id)
-      continue;
+    if (!silc_id_payload_parse_id(client_id_list.data, idp_len, &id))
+      goto out;
 
     /* Mode */
     SILC_GET32_MSB(mode, client_mode_list.data);
 
-    /* Check if we have this client cached already. */
-    client_entry = silc_client_get_client_by_id(cmd->client, conn, client_id);
-    if (!client_entry || !client_entry->username || !client_entry->realname) {
-      if (resolve) {
-       /* No we don't have it (or it is incomplete in information), query
-          it from the server. Assemble argument table that will be sent
-          for the WHOIS command later. */
-       res_argv = silc_realloc(res_argv, sizeof(*res_argv) *
-                               (res_argc + 1));
-       res_argv_lens = silc_realloc(res_argv_lens, sizeof(*res_argv_lens) *
-                                    (res_argc + 1));
-       res_argv_types = silc_realloc(res_argv_types, sizeof(*res_argv_types) *
-                                     (res_argc + 1));
-       res_argv[res_argc] = client_id_list.data;
-       res_argv_lens[res_argc] = idp_len;
-       res_argv_types[res_argc] = res_argc + 4;
-       res_argc++;
-      }
-    } else {
-      if (!silc_client_on_channel(channel, client_entry)) {
-       chu = silc_calloc(1, sizeof(*chu));
-       chu->client = client_entry;
-       chu->mode = mode;
-       chu->channel = channel;
-       silc_hash_table_add(channel->user_list, client_entry, chu);
-       silc_hash_table_add(client_entry->channels, channel, chu);
-      }
+    /* Save the client on this channel.  Unknown clients are ignored as they
+       clearly do not exist since the resolving didn't find them. */
+    client_entry = silc_client_get_client_by_id(client, conn, &id.u.client_id);
+    if (client_entry && !silc_client_on_channel(channel, client_entry)) {
+      chu = silc_calloc(1, sizeof(*chu));
+      if (!chu)
+       goto out;
+      chu->client = client_entry;
+      chu->mode = mode;
+      chu->channel = channel;
+      silc_hash_table_add(channel->user_list, client_entry, chu);
+      silc_hash_table_add(client_entry->channels, channel, chu);
     }
 
-    silc_free(client_id);
-    silc_buffer_pull(&client_id_list, idp_len);
-    silc_buffer_pull(&client_mode_list, 4);
-  }
-
-  /* Query the client information from server if the list included clients
-     that we don't know about. */
-  if (res_argc) {
-    SilcBuffer res_cmd;
-
-    /* Send the 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);
-    silc_client_packet_send(cmd->client, conn->sock, SILC_PACKET_COMMAND,
-                           NULL, 0, NULL, NULL, res_cmd->data, res_cmd->len,
-                           TRUE);
-
-    /* Register pending command callback. After we've received the WHOIS
-       command reply we will reprocess this command reply by re-calling this
-       USERS command reply callback. */
-    silc_client_command_pending(conn, SILC_COMMAND_WHOIS, conn->cmd_ident,
-                               get_clients, cmd);
-
-    silc_buffer_free(res_cmd);
-    silc_free(channel_id);
-    silc_free(res_argv);
-    silc_free(res_argv_lens);
-    silc_free(res_argv_types);
-    return 1;
-  }
-
-  if (wait_res)
-    return 1;
-
-  silc_buffer_push(&client_id_list, (client_id_list.data -
-                                    client_id_list.head));
-  silc_buffer_push(&client_mode_list, (client_mode_list.data -
-                                      client_mode_list.head));
-
-  /* Notify application */
-  if (notify)
-    COMMAND_REPLY((SILC_ARGS, channel, list_count, &client_id_list,
-                  &client_mode_list));
-
- out:
-  silc_free(channel_id);
-  return 0;
-}
-
-/* Reply to USERS command. Received list of client ID's and theirs modes
-   on the channel we requested. */
-
-SILC_CLIENT_CMD_REPLY_FUNC(users)
-{
-  SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
-  SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
-  SilcClientCommandReplyContext r = (SilcClientCommandReplyContext)context2;
-
-  SILC_LOG_DEBUG(("Start"));
-
-  if (cmd->error != SILC_STATUS_OK) {
-    SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_ERROR,
-       "Query failed: %s", silc_get_status_message(cmd->error));
-    COMMAND_REPLY_ERROR(cmd->error);
-    goto out;
-  }
-
-  if (r && !silc_command_get_status(r->payload, NULL, &cmd->error)) {
-    if (cmd->error == SILC_STATUS_ERR_NO_SUCH_CLIENT_ID) {
-      /* Do not resolve anymore. Server may be sending us some non-existent
-        Client ID (a bug in server), and we want to show the users list
-        anyway. */
-      silc_client_command_reply_users_save(cmd, cmd->status, TRUE, FALSE,
-                                          silc_client_command_reply_users_cb,
-                                          silc_client_command_reply_users);
+    if (!silc_buffer_pull(&client_id_list, idp_len))
       goto out;
-    } else {
-      SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_ERROR,
-         "Query failed: %s", silc_get_status_message(cmd->error));
-      COMMAND_REPLY_ERROR(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
+    if (!silc_buffer_pull(&client_mode_list, 4))
       goto out;
-    }
   }
 
-  if (silc_client_command_reply_users_save(cmd, cmd->status, TRUE, TRUE,
-                                          silc_client_command_reply_users_cb,
-                                          silc_client_command_reply_users))
-    return;
+  /* Notify application */
+  silc_hash_table_list(channel->user_list, &htl);
+  silc_client_command_callback(cmd, channel, &htl);
+  silc_hash_table_list_reset(&htl);
 
  out:
-  SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_USERS);
-  silc_client_command_reply_free(cmd);
+  silc_fsm_next(fsm, silc_client_command_reply_process);
+  return SILC_FSM_CONTINUE;
 }
 
+/********************************** GETKEY **********************************/
+
 /* Received command reply to GETKEY command. WE've received the remote
    client's public key. */
 
-SILC_CLIENT_CMD_REPLY_FUNC(getkey)
+SILC_FSM_STATE(silc_client_command_reply_getkey)
 {
-  SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
-  SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
-  SilcIDPayload idp = NULL;
-  SilcClientID *client_id = NULL;
+  SilcClientCommandContext cmd = fsm_context;
+  SilcClientConnection conn = cmd->conn;
+  SilcClient client = conn->client;
+  SilcCommandPayload payload = state_context;
+  SilcArgumentPayload args = silc_command_get_args(payload);
   SilcClientEntry client_entry;
-  SilcServerID *server_id = NULL;
   SilcServerEntry server_entry;
   unsigned char *tmp;
   SilcUInt32 len;
-  SilcIdType id_type;
-  SilcPublicKey public_key = NULL;
+  SilcPublicKey public_key;
+  SilcID id;
 
-  SILC_LOG_DEBUG(("Start"));
+  /* Sanity checks */
+  CHECK_STATUS("Cannot get key: ");
+  CHECK_ARGS(2, 3);
 
-  if (cmd->error != SILC_STATUS_OK) {
-    SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_ERROR,
-       "%s", silc_get_status_message(cmd->error));
-    COMMAND_REPLY_ERROR(cmd->error);
+  /* Get the ID */
+  if (!silc_argument_get_decoded(args, 2, SILC_ARGUMENT_ID, &id, NULL)) {
+    ERROR_CALLBACK(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
     goto out;
   }
 
-  tmp = silc_argument_get_arg_type(cmd->args, 2, &len);
+  /* Get the public key */
+  tmp = silc_argument_get_arg_type(args, 3, &len);
   if (!tmp) {
-    COMMAND_REPLY_ERROR(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
+    ERROR_CALLBACK(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
     goto out;
   }
-  idp = silc_id_payload_parse(tmp, len);
-  if (!idp) {
-    COMMAND_REPLY_ERROR(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
+  if (!silc_public_key_payload_decode(tmp, len, &public_key)) {
+    ERROR_CALLBACK(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
     goto out;
   }
 
-  /* Get the public key payload */
-  tmp = silc_argument_get_arg_type(cmd->args, 3, &len);
-  if (tmp) {
-    if (!silc_pkcs_public_key_payload_decode(tmp, len, &public_key))
-      public_key = NULL;
-  }
-
-  if (!public_key) {
-    COMMAND_REPLY_ERROR(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
-    goto out;
-  }
-
-  id_type = silc_id_payload_get_type(idp);
-  if (id_type == SILC_ID_CLIENT) {
+  if (id.type == SILC_ID_CLIENT) {
     /* Received client's public key */
-    client_id = silc_id_payload_get_id(idp);
-    client_entry = silc_client_get_client_by_id(cmd->client, conn, client_id);
+    client_entry = silc_client_get_client_by_id(client, conn, &id.u.client_id);
     if (!client_entry) {
-      COMMAND_REPLY_ERROR(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
+      ERROR_CALLBACK(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
       goto out;
     }
 
     /* Save fingerprint */
-    if (!client_entry->fingerprint) {
-      client_entry->fingerprint = silc_calloc(20, sizeof(unsigned char));
-      client_entry->fingerprint_len = 20;
-      silc_hash_make(cmd->client->sha1hash, tmp + 4, len - 4,
+    if (!client_entry->fingerprint)
+      silc_hash_make(client->sha1hash, tmp + 4, len - 4,
                     client_entry->fingerprint);
-    }
     if (!client_entry->public_key) {
       client_entry->public_key = public_key;
       public_key = NULL;
     }
 
     /* Notify application */
-    COMMAND_REPLY((SILC_ARGS, id_type, client_entry,
-                  client_entry->public_key));
-  } else if (id_type == SILC_ID_SERVER) {
+    silc_client_command_callback(cmd, SILC_ID_CLIENT, client_entry,
+                                client_entry->public_key);
+  } else if (id.type == SILC_ID_SERVER) {
     /* Received server's public key */
-    server_id = silc_id_payload_get_id(idp);
-    server_entry = silc_client_get_server_by_id(cmd->client, conn, server_id);
+    server_entry = silc_client_get_server_by_id(client, conn, &id.u.server_id);
     if (!server_entry) {
-      COMMAND_REPLY_ERROR(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
+      ERROR_CALLBACK(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
       goto out;
     }
 
     /* Notify application */
-    COMMAND_REPLY((SILC_ARGS, id_type, server_entry, public_key));
+    silc_client_command_callback(cmd, SILC_ID_SERVER, server_entry,
+                                public_key);
   }
 
  out:
-  SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_GETKEY);
-  if (idp)
-    silc_id_payload_free(idp);
   if (public_key)
     silc_pkcs_public_key_free(public_key);
-  silc_free(client_id);
-  silc_free(server_id);
-  silc_client_command_reply_free(cmd);
+  silc_fsm_next(fsm, silc_client_command_reply_process);
+  return SILC_FSM_CONTINUE;
 }
 
+/********************************** SERVICE *********************************/
+
 /* Reply to SERVICE command. */
 /* XXX incomplete */
 
-SILC_CLIENT_CMD_REPLY_FUNC(service)
+SILC_FSM_STATE(silc_client_command_reply_service)
 {
-  SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
+  SilcClientCommandContext cmd = fsm_context;
+  SilcCommandPayload payload = state_context;
+  SilcArgumentPayload args = silc_command_get_args(payload);
   SilcUInt32 tmp_len;
   unsigned char *service_list, *name;
 
-  COMMAND_CHECK_STATUS;
+  /* Sanity checks */
+  CHECK_STATUS("Cannot get service: ");
 
   /* Get service list */
-  service_list = silc_argument_get_arg_type(cmd->args, 2, &tmp_len);
+  service_list = silc_argument_get_arg_type(args, 2, &tmp_len);
 
   /* Get requested service name */
-  name = silc_argument_get_arg_type(cmd->args, 3, &tmp_len);
+  name = silc_argument_get_arg_type(args, 3, &tmp_len);
 
   /* Notify application */
-  COMMAND_REPLY((SILC_ARGS, service_list, name));
+  silc_client_command_callback(cmd, service_list, name);
 
- out:
-  SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_SERVICE);
- err:
-  silc_client_command_reply_free(cmd);
-}
-
-SILC_CLIENT_CMD_REPLY_FUNC(quit)
-{
-  silc_client_command_reply_free(context);
+  silc_fsm_next(fsm, silc_client_command_reply_process);
+  return SILC_FSM_CONTINUE;
 }
 
+/*********************************** QUIT ***********************************/
 
-/******************************************************************************
+/* QUIT command reply stub */
 
-                      Internal command reply functions
-
-******************************************************************************/
-
-SILC_CLIENT_CMD_REPLY_FUNC(whois_i)
+SILC_FSM_STATE(silc_client_command_reply_quit)
 {
-  SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
-  SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
-
-  COMMAND_CHECK_STATUS_I;
-
-  /* Save WHOIS info */
-  silc_client_command_reply_whois_save(cmd, cmd->status, FALSE);
-
-  /* Pending callbacks are not executed if this was an list entry */
-  if (cmd->status != SILC_STATUS_OK &&
-      cmd->status != SILC_STATUS_LIST_END) {
-    silc_client_command_reply_free(cmd);
-    return;
-  }
-
- out:
-  SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_WHOIS);
-
- err:
-  /* If we received notify for invalid ID we'll remove the ID if we
-     have it cached. */
-  if (cmd->error == SILC_STATUS_ERR_NO_SUCH_CLIENT_ID) {
-    SilcClientEntry client_entry;
-    SilcUInt32 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, NULL);
-      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;
-
-  COMMAND_CHECK_STATUS_I;
-
-  /* Save IDENTIFY info */
-  silc_client_command_reply_identify_save(cmd, cmd->status, FALSE);
-
-  /* Pending callbacks are not executed if this was an list entry */
-  if (cmd->status != SILC_STATUS_OK &&
-      cmd->status != SILC_STATUS_LIST_END) {
-    silc_client_command_reply_free(cmd);
-    return;
-  }
-
- out:
-  SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_IDENTIFY);
-
- err:
-  /* If we received notify for invalid ID we'll remove the ID if we
-     have it cached. */
-  if (cmd->error == SILC_STATUS_ERR_NO_SUCH_CLIENT_ID) {
-    SilcClientEntry client_entry;
-    SilcUInt32 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, NULL);
-      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;
-  unsigned char *tmp;
-  SilcServerEntry server;
-  SilcServerID *server_id = NULL;
-  char *server_name, *server_info;
-  SilcUInt32 len;
-
-  COMMAND_CHECK_STATUS_I;
-
-  /* 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, NULL);
-  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. */
-  server = silc_client_get_server_by_id(cmd->client, conn, server_id);
-  if (!server) {
-    SILC_LOG_DEBUG(("New server entry"));
-    silc_client_add_server(cmd->client, conn, server_name, server_info,
-                          silc_id_dup(server_id, SILC_ID_SERVER));
-  }
-
- out:
-  SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_INFO);
-  silc_free(server_id);
- err:
-  silc_client_command_reply_free(cmd);
-}
-
-static void silc_client_command_reply_users_i_cb(SilcClient client,
-                                                SilcClientConnection conn,
-                                                SilcChannelEntry *channels,
-                                                SilcUInt32 channels_count,
-                                                void *context)
-{
-  if (!channels_count) {
-    SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
-    SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
-
-    cmd->status = cmd->error = SILC_STATUS_ERR_NO_SUCH_CHANNEL;
-    SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_ERROR,
-       "%s", silc_get_status_message(cmd->error));
-    COMMAND_REPLY_ERROR(cmd->error);
-    SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_USERS);
-    silc_client_command_reply_free(cmd);
-    return;
-  }
-
-  silc_client_command_reply_users_i(context, NULL);
-}
-
-SILC_CLIENT_CMD_REPLY_FUNC(users_i)
-{
-  SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
-
-  COMMAND_CHECK_STATUS_I;
-
-  /* Save USERS info */
-  if (silc_client_command_reply_users_save(
-                                   cmd, cmd->status, FALSE, TRUE,
-                                   silc_client_command_reply_users_i_cb,
-                                   silc_client_command_reply_users_i))
-    return;
-
- out:
-  SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_USERS);
-
- err:
-  /* Unregister this command reply */
-  silc_client_command_unregister(cmd->client, SILC_COMMAND_USERS,
-                                NULL, silc_client_command_reply_users_i,
-                                cmd->ident);
-
-  silc_client_command_reply_free(cmd);
-}
-
-/* Private range commands, specific to this implementation (and compatible
-   with SILC Server >= 0.9). */
-
-SILC_CLIENT_CMD_REPLY_FUNC(connect)
-{
-  SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
-  SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
-
-  if (cmd->error != SILC_STATUS_OK) {
-    SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_ERROR,
-       "%s", silc_get_status_message(cmd->error));
-    COMMAND_REPLY_ERROR(cmd->error);
-    goto out;
-  }
-
-  /* Notify application */
-  COMMAND_REPLY((SILC_ARGS));
-
- out:
-  SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_PRIV_CONNECT);
-  silc_client_command_reply_free(cmd);
-}
-
-SILC_CLIENT_CMD_REPLY_FUNC(close)
-{
-  SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
-  SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
-
-  if (cmd->error != SILC_STATUS_OK) {
-    SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_ERROR,
-       "%s", silc_get_status_message(cmd->error));
-    COMMAND_REPLY_ERROR(cmd->error);
-    goto out;
-  }
-
-  /* Notify application */
-  COMMAND_REPLY((SILC_ARGS));
-
- out:
-  SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_PRIV_CLOSE);
-  silc_client_command_reply_free(cmd);
-}
-
-SILC_CLIENT_CMD_REPLY_FUNC(shutdown)
-{
-  SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
-  SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
-
-  if (cmd->error != SILC_STATUS_OK) {
-    SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_ERROR,
-       "%s", silc_get_status_message(cmd->error));
-    COMMAND_REPLY_ERROR(cmd->error);
-    goto out;
-  }
-
-  /* Notify application */
-  COMMAND_REPLY((SILC_ARGS));
-
- out:
-  SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_PRIV_SHUTDOWN);
-  silc_client_command_reply_free(cmd);
+  silc_fsm_next(fsm, silc_client_command_reply_process);
+  return SILC_FSM_CONTINUE;
 }
index e07fcccbfb1649cb3ea0a6c20020408081f5c91e..383f6d2e6b04b2cfa9ad5d75adeeeed9512b1348 100644 (file)
@@ -4,7 +4,7 @@
 
   Author: Pekka Riikonen <priikone@silcnet.org>
 
-  Copyright (C) 1997 - 2005 Pekka Riikonen
+  Copyright (C) 2006 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
 #ifndef COMMAND_REPLY_H
 #define COMMAND_REPLY_H
 
-/* Structure holding one command reply and pointer to its function. */
-typedef struct {
-  SilcCommandCb cb;
-  SilcCommand cmd;
-} SilcClientCommandReply;
-
-/* Context holding pending command callbacks. */
-typedef struct {
-  SilcCommandCb callback;
-  void *context;
-} *SilcClientCommandPendingCallbacks;
-
-/* Context sent as argument to all command reply functions */
-struct SilcClientCommandReplyContextStruct {
-  SilcClient client;
-  SilcSocketConnection sock;
-  SilcCommandPayload payload;
-  SilcStatus status;
-  SilcStatus error;
-  SilcArgumentPayload args;
-  SilcPacketContext *packet;
-
-  /* If defined this executes the pending command. */
-  SilcClientCommandPendingCallbacks callbacks;
-  SilcUInt32 callbacks_count;
-  SilcUInt16 ident;
-  SilcUInt8 users;
-};
-
-/* Macros */
-
-/* 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->internal->ops->command_reply args
-#define SILC_ARGS cmd->client, cmd->sock->user_data,                        \
-             cmd->payload, TRUE, silc_command_get(cmd->payload), cmd->status
-
-/* Error reply to application. Usage: COMMAND_REPLY_ERROR; */
-#define COMMAND_REPLY_ERROR(error)                                     \
-do {                                                                   \
-  if (cmd->status == SILC_STATUS_OK) {                                 \
-    cmd->client->internal->ops->                                       \
-      command_reply(cmd->client, cmd->sock->user_data, cmd->payload,   \
-                    FALSE, silc_command_get(cmd->payload), error);     \
-  } else {                                                             \
-    void *arg1 = NULL, *arg2 = NULL;                                   \
-    silc_status_get_args(cmd->status, cmd->args, &arg1, &arg2);                \
-    cmd->client->internal->ops->                                       \
-      command_reply(cmd->client, cmd->sock->user_data, cmd->payload,   \
-                    FALSE, silc_command_get(cmd->payload), cmd->status,        \
-                   arg1, arg2);                                        \
-    silc_free(arg1);                                                   \
-    silc_free(arg2);                                                   \
-  }                                                                    \
-} while(0)
-
-/* Macro used to declare command reply functions */
-#define SILC_CLIENT_CMD_REPLY_FUNC(func)                               \
-void silc_client_command_reply_##func(void *context, void *context2)
-
-/* Prototypes */
-
-void silc_client_command_reply_process(SilcClient client,
-                                      SilcSocketConnection sock,
-                                      SilcPacketContext *packet);
-void silc_client_command_reply_free(SilcClientCommandReplyContext cmd);
-SILC_CLIENT_CMD_REPLY_FUNC(whois);
-SILC_CLIENT_CMD_REPLY_FUNC(whowas);
-SILC_CLIENT_CMD_REPLY_FUNC(identify);
-SILC_CLIENT_CMD_REPLY_FUNC(nick);
-SILC_CLIENT_CMD_REPLY_FUNC(list);
-SILC_CLIENT_CMD_REPLY_FUNC(topic);
-SILC_CLIENT_CMD_REPLY_FUNC(invite);
-SILC_CLIENT_CMD_REPLY_FUNC(kill);
-SILC_CLIENT_CMD_REPLY_FUNC(info);
-SILC_CLIENT_CMD_REPLY_FUNC(stats);
-SILC_CLIENT_CMD_REPLY_FUNC(ping);
-SILC_CLIENT_CMD_REPLY_FUNC(oper);
-SILC_CLIENT_CMD_REPLY_FUNC(join);
-SILC_CLIENT_CMD_REPLY_FUNC(motd);
-SILC_CLIENT_CMD_REPLY_FUNC(umode);
-SILC_CLIENT_CMD_REPLY_FUNC(cmode);
-SILC_CLIENT_CMD_REPLY_FUNC(cumode);
-SILC_CLIENT_CMD_REPLY_FUNC(kick);
-SILC_CLIENT_CMD_REPLY_FUNC(ban);
-SILC_CLIENT_CMD_REPLY_FUNC(detach);
-SILC_CLIENT_CMD_REPLY_FUNC(watch);
-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(service);
-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);
-SILC_CLIENT_CMD_REPLY_FUNC(users_i);
-
-SILC_CLIENT_CMD_REPLY_FUNC(connect);
-SILC_CLIENT_CMD_REPLY_FUNC(close);
-SILC_CLIENT_CMD_REPLY_FUNC(shutdown);
-
-#endif
+SILC_FSM_STATE(silc_client_command_reply);
+SILC_FSM_STATE(silc_client_command_reply_wait);
+SILC_FSM_STATE(silc_client_command_reply_process);
+SILC_FSM_STATE(silc_client_command_reply_whois);
+SILC_FSM_STATE(silc_client_command_reply_whowas);
+SILC_FSM_STATE(silc_client_command_reply_identify);
+SILC_FSM_STATE(silc_client_command_reply_nick);
+SILC_FSM_STATE(silc_client_command_reply_list);
+SILC_FSM_STATE(silc_client_command_reply_topic);
+SILC_FSM_STATE(silc_client_command_reply_invite);
+SILC_FSM_STATE(silc_client_command_reply_kill);
+SILC_FSM_STATE(silc_client_command_reply_info);
+SILC_FSM_STATE(silc_client_command_reply_stats);
+SILC_FSM_STATE(silc_client_command_reply_ping);
+SILC_FSM_STATE(silc_client_command_reply_oper);
+SILC_FSM_STATE(silc_client_command_reply_join);
+SILC_FSM_STATE(silc_client_command_reply_motd);
+SILC_FSM_STATE(silc_client_command_reply_umode);
+SILC_FSM_STATE(silc_client_command_reply_cmode);
+SILC_FSM_STATE(silc_client_command_reply_cumode);
+SILC_FSM_STATE(silc_client_command_reply_kick);
+SILC_FSM_STATE(silc_client_command_reply_ban);
+SILC_FSM_STATE(silc_client_command_reply_detach);
+SILC_FSM_STATE(silc_client_command_reply_watch);
+SILC_FSM_STATE(silc_client_command_reply_silcoper);
+SILC_FSM_STATE(silc_client_command_reply_leave);
+SILC_FSM_STATE(silc_client_command_reply_users);
+SILC_FSM_STATE(silc_client_command_reply_getkey);
+SILC_FSM_STATE(silc_client_command_reply_service);
+SILC_FSM_STATE(silc_client_command_reply_quit);
+
+#endif /* COMMAND_REPLY_H */
index c16d9aa3545570e952a347d05f395b51358f7c85..078c832f2b4dcc82bc9c25713f919ac950c6ae57 100644 (file)
@@ -4,7 +4,7 @@
 
   Author: Pekka Riikonen <priikone@silcnet.org>
 
-  Copyright (C) 1997 - 2004 Pekka Riikonen
+  Copyright (C) 1997 - 2004, 2006 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
@@ -1185,27 +1185,3 @@ SILC_TASK_CALLBACK(silc_client_protocol_rekey)
   }
 
 }
-
-/* Registers protocols used in client */
-
-void silc_client_protocols_register(void)
-{
-  silc_protocol_register(SILC_PROTOCOL_CLIENT_CONNECTION_AUTH,
-                        silc_client_protocol_connection_auth);
-  silc_protocol_register(SILC_PROTOCOL_CLIENT_KEY_EXCHANGE,
-                        silc_client_protocol_key_exchange);
-  silc_protocol_register(SILC_PROTOCOL_CLIENT_REKEY,
-                        silc_client_protocol_rekey);
-}
-
-/* Unregisters protocols */
-
-void silc_client_protocols_unregister(void)
-{
-  silc_protocol_unregister(SILC_PROTOCOL_CLIENT_CONNECTION_AUTH,
-                          silc_client_protocol_connection_auth);
-  silc_protocol_unregister(SILC_PROTOCOL_CLIENT_KEY_EXCHANGE,
-                          silc_client_protocol_key_exchange);
-  silc_protocol_unregister(SILC_PROTOCOL_CLIENT_REKEY,
-                          silc_client_protocol_rekey);
-}
index 678f01daa7e3aee3fcd325d37e29c7d883cf92ac..56852d83361230f5ed4a41f356571701dab44da5 100644 (file)
@@ -4,7 +4,7 @@
 
   Author: Pekka Riikonen <priikone@silcnet.org>
 
-  Copyright (C) 2000 - 2005 Pekka Riikonen
+  Copyright (C) 2000 - 2006 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
@@ -52,9 +52,62 @@ extern "C" {
 #endif
 
 #include "client.h"
+#include "silcclient_entry.h"
 
 /* General definitions */
 
+/****d* silcclient/SilcClientAPI/SilcClientConnectionStatus
+ *
+ * NAME
+ *
+ *    typedef enum { ... } SilcClientConnectionStatus
+ *
+ * DESCRIPTION
+ *
+ *    This type is returned to the `connect' client operation to indicate
+ *    the status of the created connection.  It can indicate if it was
+ *    successful or whether an error occurred.
+ *
+ * SOURCE
+ */
+typedef enum {
+  SILC_CLIENT_CONN_SUCCESS,           /* Successfully connected */
+  SILC_CLIENT_CONN_SUCCESS_RESUME,     /* Successfully connected and
+                                         resumed old detached session */
+  SILC_CLIENT_CONN_DISCONNECTED,       /* Remote host disconnected */
+  SILC_CLIENT_CONN_ERROR,             /* Error occurred during connecting */
+  SILC_CLIENT_CONN_ERROR_KE,          /* Key Exchange failed */
+  SILC_CLIENT_CONN_ERROR_AUTH,        /* Authentication failed */
+  SILC_CLIENT_CONN_ERROR_RESUME,       /* Resuming failed */
+  SILC_CLIENT_CONN_ERROR_TIMEOUT,      /* Timeout during connecting */
+} SilcClientConnectionStatus;
+/***/
+
+/****f* silcclient/SilcClientAPI/SilcClientConnectCallback
+ *
+ * SYNOPSIS
+ *
+ *    void (*SilcClientConnectCallback)(SilcClient client,
+ *                                      SilcClientConnection conn,
+ *                                      SilcClientConnectionStatus status,
+ *                                      void *context);
+ *
+ * DESCRIPTION
+ *
+ *    Connect callbak given as argument to silc_client_connect_to_server,
+ *    silc_client_connect_to_client and silc_client_add_connection functions.
+ *    It is called to indicate the status of the connection, indicated
+ *    by the `status'.  It is called after the connection has been
+ *    established to the remote host and when connection is disconnected
+ *    by the remote host.  The `context' is the context given as argument
+ *    to the connecting function.
+ *
+ ***/
+typedef void (*SilcClientConnectCallback)(SilcClient client,
+                                         SilcClientConnection conn,
+                                         SilcClientConnectionStatus status,
+                                         void *context);
+
 /****s* silcclient/SilcClientAPI/SilcClient
  *
  * NAME
@@ -81,10 +134,6 @@ struct SilcClientStruct {
   char *hostname;               /* hostname, MUST be set by application */
   char *realname;               /* Real name, MUST be set be application */
 
-  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 */
-
   /*
    * The following fields are set by the library
    */
@@ -102,6 +151,7 @@ struct SilcClientStruct {
   void *application;
 
   /* Generic hash context for application usage */
+  /* XXX remove these; not thread safe */
   SilcHash md5hash;
   SilcHash sha1hash;
 
@@ -132,160 +182,36 @@ struct SilcClientConnectionStruct {
   /*
    * Local data
    */
-  char *nickname;                /* Current nickname */
-  SilcClientEntry local_entry;   /* Own Client Entry */
-  SilcClientID *local_id;        /* Current Client ID */
-  unsigned char *local_id_data;          /* Current Client ID decoded */
-  SilcUInt32 local_id_data_len;
+  SilcClientEntry local_entry;        /* Own Client Entry */
+  SilcClientID *local_id;             /* Current Client ID */
+  SilcBuffer local_idp;                       /* Current Client ID Payload */
 
   /*
    * Remote data
    */
-  char *remote_host;             /* Remote host name, UTF-8 encoded */
-  int remote_port;               /* Remote port */
-  SilcServerID *remote_id;       /* Remote Server ID */
-  unsigned char *remote_id_data;  /* Remote Server ID decoded */
-  SilcUInt32 remote_id_data_len;
+  char *remote_host;                  /* Remote host name, UTF-8 encoded */
+  int remote_port;                    /* Remote port */
+  SilcID remote_id;                   /* Remote ID */
+  SilcBuffer remote_idp;              /* Remote ID Payload */
 
   /*
    * Common data
    */
 
-  /* Current command identifier for a command that was sent last.
-     Application may get the value from this variable to find out the
-     command identifier for last command. */
-  SilcUInt16 cmd_ident;
-
-  /* User data context. Library does not touch this. Application may
-     freely set and use this pointer for its needs. */
-  void *context;
-
-  /* Pointer back to the SilcClient.  Application may use this. */
-  SilcClient client;
+  SilcChannelEntry current_channel;    /* Current joined channel */
+  SilcPublicKey public_key;           /* Public key used in this connection */
+  SilcPrivateKey private_key;         /* Private key */
+  SilcPacketStream stream;            /* Connection to remote host */
+  SilcConnectionType type;            /* Connection type */
+  SilcClientConnectCallback callback;  /* Connection callback */
+  void *context;                      /* Connection context */
+  SilcClient client;                  /* Pointer back to SilcClient */
 
-  /* Current channel.  Application may use and set this pointer if needed. */
-  SilcChannelEntry current_channel;
-
-  /* Socket connection object for this connection.  Application may
-     use this if needed.  The sock->user_data is back pointer to this
-     structure. */
-  SilcSocketConnection sock;
-
-  /* Internal data for client library. Application cannot access this
-     data at all. */
+  /* Internal data for client library.  Application cannot access this. */
   SilcClientConnectionInternal internal;
 };
 /***/
 
-/****s* silcclient/SilcClientAPI/SilcClientEntry
- *
- * NAME
- *
- *    typedef struct SilcClientEntryStruct { ... } *SilcClientEntry
- *
- * DESCRIPTION
- *
- *    This structure represents a client or a user in the SILC network.
- *    The local user has this structure also and it can be accessed from
- *    SilcClientConnection structure.  All other users in the SILC network
- *    that are accessed using the Client Library routines will have their
- *    own SilcClientEntry structure.  For example, when finding users by
- *    their nickname the Client Library returns this structure back to
- *    the application.  All strings in the structure are UTF-8 encoded.
- *
- * SOURCE
- */
-struct SilcClientEntryStruct {
-  /* General information */
-  char *nickname;              /* nickname */
-  char *username;              /* username */
-  char *hostname;              /* hostname */
-  char *server;                        /* SILC server name */
-  char *realname;              /* Realname (userinfo) */
-
-  /* Mode, ID and other information */
-  SilcUInt32 mode;             /* User mode in SILC, see SilcUserMode */
-  SilcClientID *id;            /* The Client ID */
-  SilcDList attrs;             /* Requested Attributes (maybe NULL) */
-  unsigned char *fingerprint;  /* Fingerprint of client's public key */
-  SilcUInt32 fingerprint_len;  /* Length of the fingerprint */
-  SilcPublicKey public_key;    /* User's public key, may be NULL */
-
-  /* Private message keys */
-  SilcCipher send_key;         /* Private message key for sending */
-  SilcCipher receive_key;      /* Private message key for receiving */
-  SilcHmac hmac_send;          /* Private mesage key HMAC for sending */
-  SilcHmac hmac_receive;       /* Private mesage key HMAC for receiving */
-  unsigned char *key;          /* Set only if application provided the
-                                  key material. NULL if the library
-                                  generated the key. */
-  SilcUInt32 key_len;          /* Key length */
-  SilcClientKeyAgreement ke;   /* Current key agreement context or NULL */
-
-  /* SilcClientEntry status information */
-  SilcEntryStatus status;      /* Status mask */
-  SilcHashTable channels;      /* All channels client has joined */
-  SilcUInt16 resolve_cmd_ident;        /* Command identifier when resolving */
-  unsigned int generated   : 1; /* TRUE if library generated `key' */
-  unsigned int valid       : 1;        /* FALSE if this entry is not valid */
-  unsigned int prv_resp    : 1; /* TRUE if private message key indicator
-                                  has been received (responder). */
-
-  /* Application specific data.  Application may set here whatever it wants. */
-  void *context;
-};
-/***/
-
-/****s* silcclient/SilcClientAPI/SilcChannelEntry
- *
- * NAME
- *
- *    typedef struct SilcChannelEntryStruct { ... } *SilcChannelEntry
- *
- * DESCRIPTION
- *
- *    This structure represents a channel in the SILC network.  All
- *    channels that the client are aware of or have joined in will be
- *    represented as SilcChannelEntry.  The structure includes information
- *    about the channel.  All strings in the structure are UTF-8 encoded.
- *
- * SOURCE
- */
-struct SilcChannelEntryStruct {
-  /* General information */
-  char *channel_name;                       /* Channel name */
-  SilcChannelID *id;                        /* Channel ID */
-  SilcUInt32 mode;                          /* Channel mode, ChannelModes. */
-  char *topic;                              /* Current topic, may be NULL */
-  SilcPublicKey founder_key;                /* Founder key, may be NULL */
-  SilcUInt32 user_limit;                    /* User limit on channel */
-
-  /* All clients that has joined this channel.  The key to the table is the
-     SilcClientEntry and the context is SilcChannelUser context. */
-  SilcHashTable user_list;
-
-  /* Channel keys */
-  SilcCipher channel_key;                    /* The channel key */
-  unsigned char *key;                       /* Raw key data */
-  SilcUInt32 key_len;                       /* Raw key data length */
-  unsigned char iv[SILC_CIPHER_MAX_IV_SIZE]; /* Current IV */
-  SilcHmac hmac;                            /* Current HMAC */
-
-  /* Channel private keys */
-  SilcDList private_keys;                   /* List of private keys or NULL */
-  SilcChannelPrivateKey curr_key;           /* Current private key */
-
-  /* SilcChannelEntry status information */
-  SilcDList old_channel_keys;
-  SilcDList old_hmacs;
-  SilcUInt16 resolve_cmd_ident;                     /* Command identifier when
-                                               resolving this entry */
-
-  /* Application specific data.  Application may set here whatever it wants. */
-  void *context;
-};
-/***/
-
 /****s* silcclient/SilcClientAPI/SilcChannelUser
  *
  * NAME
@@ -310,32 +236,36 @@ struct SilcChannelUserStruct {
 };
 /***/
 
-/****s* silcclient/SilcClientAPI/SilcServerEntry
+/****s* silcclient/SilcClientAPI/SilcClientStats
  *
  * NAME
  *
- *    typedef struct SilcServerEntryStruct { ... } *SilcServerEntry
+ *    typedef struct { ... } SilcClientStats;
  *
  * DESCRIPTION
  *
- *    This structure represents a server in the SILC network.  All servers
- *    that the client is aware of and have for example resolved with
- *    SILC_COMMAND_INFO command have their on SilcServerEntry structure.
- *    All strings in the structure are UTF-8 encoded.
+ *    This structure holds SILC network statistics returned by the
+ *    SILC_COMMAND_STATS command reply to the application.
  *
  * SOURCE
  */
-struct SilcServerEntryStruct {
-  /* General information */
-  char *server_name;                        /* Server name */
-  char *server_info;                        /* Server info */
-  SilcServerID *server_id;                  /* Server ID */
-  SilcUInt16 resolve_cmd_ident;                     /* Command identifier when
-                                               resolving this entry */
-
-  /* Application specific data.  Application may set here whatever it wants. */
-  void *context;
-};
+typedef struct {
+  SilcUInt32 starttime;                /* SILC server start time */
+  SilcUInt32 uptime;           /* SILC server uptime*/
+  SilcUInt32 my_clients;       /* Number of clients in the server */
+  SilcUInt32 my_channels;      /* Number of channel in the server */
+  SilcUInt32 my_server_ops;    /* Number of server operators in the server */
+  SilcUInt32 my_router_ops;    /* Number of router operators in the router */
+  SilcUInt32 cell_clients;     /* Number of clients in the cell */
+  SilcUInt32 cell_channels;    /* Number of channels in the cell */
+  SilcUInt32 cell_servers;     /* Number of server in the cell */
+  SilcUInt32 clients;          /* All clients in SILC network */
+  SilcUInt32 channels;         /* All channels in SILC network */
+  SilcUInt32 servers;          /* All servers in SILC network */
+  SilcUInt32 routers;          /* All routers in SILC network */
+  SilcUInt32 server_ops;       /* All server operators in SILC network */
+  SilcUInt32 router_ops;       /* All router operators in SILC network */
+} SilcClientStats;
 /***/
 
 /****d* silcclient/SilcClientAPI/SilcKeyAgreementStatus
@@ -481,9 +411,9 @@ typedef void (*SilcVerifyPublicKey)(SilcBool success, void *context);
  * SYNOPSIS
  *
  *    typedef void (*SilcGetAuthMeth)(SilcBool success,
- *                                    SilcProtocolAuthMeth auth_meth,
- *                                    const unsigned char *auth_data,
- *                                    SilcUInt32 auth_data_len, void *context);
+ *                                    SilcAuthMethod auth_meth,
+ *                                    void *auth, SilcUInt32 auth_len,
+ *                                    void *context);
  *
  * DESCRIPTION
  *
@@ -498,9 +428,9 @@ typedef void (*SilcVerifyPublicKey)(SilcBool success, void *context);
  *
  ***/
 typedef void (*SilcGetAuthMeth)(SilcBool success,
-                               SilcProtocolAuthMeth auth_meth,
-                               const unsigned char *auth_data,
-                               SilcUInt32 auth_data_len, void *context);
+                               SilcAuthMethod auth_meth,
+                               void *auth, SilcUInt32 auth_len,
+                               void *context);
 
 /****d* silcclient/SilcClientAPI/SilcClientMessageType
  *
@@ -523,33 +453,6 @@ typedef enum {
 } SilcClientMessageType;
 /***/
 
-/****d* silcclient/SilcClientAPI/SilcClientConnectionStatus
- *
- * NAME
- *
- *    typedef enum { ... } SilcClientConnectionStatus
- *
- * DESCRIPTION
- *
- *    This type is returned to the `connect' client operation to indicate
- *    the status of the created connection.  It can indicate if it was
- *    successful or whether an error occurred.
- *
- * SOURCE
- */
-typedef enum {
-  SILC_CLIENT_CONN_SUCCESS,           /* Successfully connected */
-  SILC_CLIENT_CONN_SUCCESS_RESUME,     /* Successfully connected and
-                                         resumed old detached session */
-  SILC_CLIENT_CONN_ERROR,             /* Unknown error occurred during
-                                         connecting */
-  SILC_CLIENT_CONN_ERROR_KE,          /* Key Exchange failed */
-  SILC_CLIENT_CONN_ERROR_AUTH,        /* Authentication failed */
-  SILC_CLIENT_CONN_ERROR_RESUME,       /* Resuming failed */
-  SILC_CLIENT_CONN_ERROR_TIMEOUT,      /* Timeout during connecting */
-} SilcClientConnectionStatus;
-/***/
-
 /****s* silcclient/SilcClientAPI/SilcClientOperations
  *
  * NAME
@@ -609,17 +512,18 @@ typedef struct {
   void (*notify)(SilcClient client, SilcClientConnection conn,
                 SilcNotifyType type, ...);
 
-  /* Command handler. This function is called always in the command function.
-     If error occurs it will be called as well. `conn' is the associated
-     client connection. `cmd_context' is the command context that was
-     originally sent to the command. `success' is FALSE if error occurred
-     during command. `command' is the command being processed. It must be
-     noted that this is not reply from server. This is merely called just
-     after application has called the command. Just to tell application
-     that the command really was processed. */
+  /* Command handler. This function is called always after application has
+     called a command.  It will be called to indicate that the command
+     was processed.  It will also be called if error occurs while processing
+     the command.  The `success' indicates whether the command was sent
+     or if error occurred.  The `status' indicates the actual error.
+     The `argc' and `argv' are the command line arguments sent to the
+     command by application.  Note that, this is not reply to the command
+     from server, this is merely and indication to application that the
+     command was processed. */
   void (*command)(SilcClient client, SilcClientConnection conn,
-                 SilcClientCommandContext cmd_context, SilcBool success,
-                 SilcCommand command, SilcStatus status);
+                 SilcBool success, SilcCommand command, SilcStatus status,
+                 SilcUInt32 argc, unsigned char **argv);
 
   /* Command reply handler. This function is called always in the command reply
      function. If error occurs it will be called as well. Normal scenario
@@ -644,26 +548,8 @@ typedef struct {
      ID. For example, if Client ID is receives application receives
      SilcClientEntry. */
   void (*command_reply)(SilcClient client, SilcClientConnection conn,
-                       SilcCommandPayload cmd_payload, SilcBool success,
-                       SilcCommand command, SilcStatus status, ...);
-
-  /* Called to indicate that connection was either successfully established
-     or connecting failed.  This is also the first time application receives
-     the SilcClientConnection object which it should save somewhere.
-     The `status' indicated whether the connection were successful.  If it
-     is error value the application must always call the function
-     silc_client_close_connection. */
-  void (*connected)(SilcClient client, SilcClientConnection conn,
-                   SilcClientConnectionStatus status);
-
-  /* Called to indicate that connection was disconnected to the server.
-     The `status' may tell the reason of the disconnection, and if the
-     `message' is non-NULL it may include the disconnection message
-     received from server. Application must not call the
-     silc_client_close_connection in this callback.  The 'conn' is also
-     invalid after this function returns back to library. */
-  void (*disconnected)(SilcClient client, SilcClientConnection conn,
-                      SilcStatus status, const char *message);
+                       SilcCommand command, SilcStatus status,
+                       SilcStatus error, va_list ap);
 
   /* Find authentication method and authentication data by hostname and
      port. The hostname may be IP address as well. When the authentication
@@ -680,8 +566,8 @@ typedef struct {
      use. The `completion' must be called after the public key has been
      verified. */
   void (*verify_public_key)(SilcClient client, SilcClientConnection conn,
-                           SilcSocketType conn_type, unsigned char *pk,
-                           SilcUInt32 pk_len, SilcSKEPKType pk_type,
+                           SilcConnectionType conn_type,
+                           SilcPublicKey public_key,
                            SilcVerifyPublicKey completion, void *context);
 
   /* Ask (interact, that is) a passphrase from user. The passphrase is
@@ -691,16 +577,6 @@ typedef struct {
   void (*ask_passphrase)(SilcClient client, SilcClientConnection conn,
                         SilcAskPassphrase completion, void *context);
 
-  /* Notifies application that failure packet was received.  This is called
-     if there is some protocol active in the client.  The `protocol' is the
-     protocol context.  The `failure' is opaque pointer to the failure
-     indication.  Note, that the `failure' is protocol dependant and
-     application must explicitly cast it to correct type.  Usually `failure'
-     is 32 bit failure type (see protocol specs for all protocol failure
-     types). */
-  void (*failure)(SilcClient client, SilcClientConnection conn,
-                 SilcProtocol protocol, void *failure);
-
   /* Asks whether the user would like to perform the key agreement protocol.
      This is called after we have received an key agreement packet or an
      reply to our key agreement packet. This returns TRUE if the user wants
@@ -709,9 +585,10 @@ typedef struct {
      silc_client_perform_key_agreement). If TRUE is returned also the
      `completion' and `context' arguments must be set by the application. */
   SilcBool (*key_agreement)(SilcClient client, SilcClientConnection conn,
-                       SilcClientEntry client_entry, const char *hostname,
-                       SilcUInt16 port, SilcKeyAgreementCallback *completion,
-                       void **context);
+                           SilcClientEntry client_entry,
+                           const char *hostname, SilcUInt16 port,
+                           SilcKeyAgreementCallback *completion,
+                           void **context);
 
   /* Notifies application that file transfer protocol session is being
      requested by the remote client indicated by the `client_entry' from
@@ -742,6 +619,10 @@ typedef struct {
   void (*detach)(SilcClient client, SilcClientConnection conn,
                 const unsigned char *detach_data,
                 SilcUInt32 detach_data_len);
+
+  /* Called when the client library is up and running.  After this callback
+     is called the application may start using the client library APIs. */
+  void (*running)(SilcClient client, void *application);
 } SilcClientOperations;
 /***/
 
@@ -779,6 +660,13 @@ typedef void (*SilcNicknameFormatParse)(const char *nickname,
  * SOURCE
  */
 typedef struct {
+  /* If this boolean is set to TRUE then the client library will use
+     threads.  Any of the callback functions in the SilcClientOperations
+     and other callbacks may be called at any time in a thread.  The
+     application may need to employ appropriate concurrency control
+     in the callbacks to protect application specific data. */
+  SilcBool threads;
+
   /* Number of maximum tasks the client library's scheduler can handle.
      If set to zero, the default value will be used (200). For WIN32
      systems this should be set to 64 as it is the hard limit dictated
@@ -928,7 +816,7 @@ SilcBool silc_client_init(SilcClient client);
  * DESCRIPTION
  *
  *    Runs the client. This starts the scheduler from the utility library.
- *    When this functions returns the execution of the appliation is over.
+ *    When this functions returns the execution of the application is over.
  *    The client must be initialized before calling this.
  *
  ***/
@@ -969,7 +857,7 @@ void silc_client_run_one(SilcClient client);
 void silc_client_stop(SilcClient client);
 
 
-/* Connecting functions (client.c) */
+/* Connecting functions */
 
 /****s* silcclient/SilcClientAPI/SilcClientConnectionParams
  *
@@ -986,6 +874,49 @@ void silc_client_stop(SilcClient client);
  * SOURCE
  */
 typedef struct {
+  /* If this key repository pointer is non-NULL then public key received in
+     the key exchange protocol will be verified from this repository.  If
+     this is not provided then the `verify_public_key' client operation will
+     be called back to application.  If the boolean `verify_notfound' is set
+     to TRUE then the `verify_public_key' client operation will be called
+     in case the public key is not found in `repository'. */
+  SilcSKR repository;
+  SilcBool verify_notfound;
+
+  /* Authentication data.  Application may set here the authentication data
+     and authentication method to be used in connecting.  If `auth_set'
+     boolean is TRUE then authentication data is provided by application.
+     If the authentication method is public key authentication then the key
+     pair given as argument when connecting will be used and `auth' field
+     is NULL.  If it is passphrase authentication, it can be provided in
+     `auth' and `auth_len' fields.  If `auth_set' is FALSE
+     the `get_auth_method' client operation will be called to get the
+     authentication method and data from application. */
+  SilcBool auth_set;
+  SilcAuthMethod auth_method;
+  void *auth;
+  SilcUInt32 auth_len;
+
+  /* If this boolean is set to TRUE then the client's connection to the
+     remote host will use UDP instead of TCP.  The `local_ip' specifies
+     the local IP address used with the UDP connection, and it must be
+     non-NULL.  If the `local_port' is non-zero it will be used as local
+     port with the UDP connection.  The remote host will also send packets
+     to the specified address and port. */
+  SilcBool udp;
+  char *local_ip;
+  int local_port;
+
+  /* If this boolean is set to TRUE then the key exchange is done with
+     perfect forward secrecy. */
+  SilcBool pfs;
+
+  /* If this boolean is set to TRUE then connection authentication protocol
+     is not performed during connecting.  Only key exchange protocol is
+     performed.  This usually must be set to TRUE when connecting to another
+     client, but must be FALSE with server connections. */
+  SilcBool no_authentication;
+
   /* The SILC session detachment data that was returned by `detach' client
      operation when the application detached from the network.  Application
      is responsible of saving the data and giving it as argument here
@@ -1007,26 +938,74 @@ typedef struct {
  *
  * SYNOPSIS
  *
- *    int silc_client_connect_to_server(SilcClient client,
- *                                      SilcClientConnectionParams *params,
- *                                      int port, char *host, void *context);
- *
- * DESCRIPTION
+ *    void
+ *    silc_client_connect_to_server(SilcClient client,
+ *                                  SilcClientConnectionParams *params,
+ *                                  SilcPublicKey public_key,
+ *                                  char *remote_host, int port,
+ *                                  SilcClientConnectCallback callback,
+ *                                  void *context);
+ *
+ * DESCRIPTION
+ *
+ *    Connects to remote server `remote_host' at port `port'.  This function
+ *    can be used to create connection to remote SILC server and start
+ *    SILC session in the SILC network.  The `params' may be provided
+ *    to provide various connection parameters.  The `public_key' and the
+ *    `private_key' is your identity used in this connection.  When
+ *    authentication method is based on digital signatures, this key pair
+ *    will be used.  The `callback' with `context' will be called after the
+ *    connection has been created.  It will also be called later when remote
+ *    host disconnects.
+ *
+ *    If application wishes to create the network connection itself, use
+ *    the silc_client_add_connection after creating the connection, instead
+ *    of using this function.
+ *
+ ***/
+void silc_client_connect_to_server(SilcClient client,
+                                  SilcClientConnectionParams *params,
+                                  SilcPublicKey public_key,
+                                  SilcPrivateKey private_key,
+                                  char *remote_host, int port,
+                                  SilcClientConnectCallback callback,
+                                  void *context);
+
+/****f* silcclient/SilcClientAPI/silc_client_connect_to_client
  *
- *    Connects to remote server. This is the main routine used to connect
- *    to SILC server. Returns -1 on error and the created socket otherwise.
- *    The `context' is user context that is saved into the SilcClientConnection
- *    that is created after the connection is created. Note that application
- *    may handle the connecting process outside the library. If this is the
- *    case then this function is not used at all. When the connecting is
- *    done the `connect' client operation is called, and the `context' is
- *    accessible with conn->context, conn being SilcClientConnection.
- *    If the `params' is provided they are used by the routine.
+ * SYNOPSIS
  *
- ***/
-int silc_client_connect_to_server(SilcClient client,
-                                 SilcClientConnectionParams *params,
-                                 int port, char *host, void *context);
+ *    void
+ *    silc_client_connect_to_client(SilcClient client,
+ *                                  SilcClientConnectionParams *params,
+ *                                  SilcPublicKey public_key,
+ *                                  char *remote_host, int port,
+ *                                  SilcClientConnectCallback callback,
+ *                                  void *context);
+ *
+ * DESCRIPTION
+ *
+ *    Connects to remote client `remote_host' at port `port'.  This function
+ *    can be used to create peer-to-peer connection to another SILC client,
+ *    for example, for direct conferencing, or file transfer or for other
+ *    purposes.  The `params' may be provided to provide various connection
+ *    parameters.  The `public_key' and the `private_key' is your identity
+ *    used in this connection.  The `callback' with `context' will be called
+ *    after the connection has been created.  It will also be called later
+ *    when remote host disconnects.
+ *
+ *    If application wishes to create the network connection itself, use
+ *    the silc_client_add_connection after creating the connection, instead
+ *    of using this function.
+ *
+ ***/
+void silc_client_connect_to_client(SilcClient client,
+                                  SilcClientConnectionParams *params,
+                                  SilcPublicKey public_key,
+                                  SilcPrivateKey private_key,
+                                  char *remote_host, int port,
+                                  SilcClientConnectCallback callback,
+                                  void *context);
 
 /****f* silcclient/SilcClientAPI/silc_client_add_connection
  *
@@ -1035,6 +1014,7 @@ int silc_client_connect_to_server(SilcClient client,
  *
  *    SilcClientConnection
  *    silc_client_add_connection(SilcClient client,
+ *                               SilcConnectionType conn_type,
  *                               SilcClientConnectionParams *params,
  *                               char *hostname, int port, void *context);
  *
@@ -1042,7 +1022,7 @@ int silc_client_connect_to_server(SilcClient client,
  *
  *    Allocates and adds new connection to the client. This adds the allocated
  *    connection to the connection table and returns a pointer to it. A client
- *    can have multiple connections to multiple servers. Every connection must
+ *    can have multiple connections to multiple hosts. Every connection must
  *    be added to the client using this function. User data `context' may
  *    be sent as argument.  If the `params' is provided they are used by
  *    the routine.
@@ -1051,14 +1031,18 @@ int silc_client_connect_to_server(SilcClient client,
  *
  *    This function is normally used only if the application performed
  *    the connecting outside the library, and did not called the
- *    silc_client_connect_to_server function at all. The library
- *    however may use this internally.
+ *    silc_client_connect_to_server function at all.
  *
  ***/
 SilcClientConnection
 silc_client_add_connection(SilcClient client,
+                          SilcConnectionType conn_type,
                           SilcClientConnectionParams *params,
-                          char *hostname, int port, void *context);
+                          SilcPublicKey public_key,
+                          SilcPrivateKey private_key,
+                          char *remote_host, int port,
+                          SilcClientConnectCallback callback,
+                          void *context);
 
 /****f* silcclient/SilcClientAPI/silc_client_del_connection
  *
@@ -1077,45 +1061,13 @@ silc_client_add_connection(SilcClient client,
  ***/
 void silc_client_del_connection(SilcClient client, SilcClientConnection conn);
 
-/****f* silcclient/SilcClientAPI/silc_client_add_socket
- *
- * SYNOPSIS
- *
- *    void silc_client_add_socket(SilcClient client,
- *                                SilcSocketConnection sock);
- *
- * DESCRIPTION
- *
- *    Adds listener socket to the listener sockets table. This function is
- *    used to add socket objects that are listeners to the client.  This should
- *    not be used to add other connection objects.
- *
- ***/
-void silc_client_add_socket(SilcClient client, SilcSocketConnection sock);
-
-/****f* silcclient/SilcClientAPI/silc_client_del_socket
- *
- * SYNOPSIS
- *
- *    void silc_client_del_socket(SilcClient client,
- *                                SilcSocketConnection sock);
- *
- * DESCRIPTION
- *
- *    Deletes listener socket from the listener sockets table.  If the
- *    application has added a socket with silc_client_add_socket it must
- *    also free it using this function.
- *
- ***/
-void silc_client_del_socket(SilcClient client, SilcSocketConnection sock);
-
 /****f* silcclient/SilcClientAPI/silc_client_start_key_exchange
  *
  * SYNOPSIS
  *
  *    void silc_client_start_key_exchange(SilcClient client,
  *                                        SilcClientConnection conn,
- *                                        int fd);
+ *                                        SilcStream stream);
  *
  * DESCRIPTION
  *
@@ -1136,7 +1088,7 @@ void silc_client_del_socket(SilcClient client, SilcSocketConnection sock);
  ***/
 void silc_client_start_key_exchange(SilcClient client,
                                    SilcClientConnection conn,
-                                   int fd);
+                                   SilcStream stream);
 
 /****f* silcclient/SilcClientAPI/silc_client_close_connection
  *
@@ -1166,13 +1118,12 @@ void silc_client_close_connection(SilcClient client,
  * SYNOPSIS
  *
  *    SilcBool silc_client_send_channel_message(SilcClient client,
- *                                          SilcClientConnection conn,
- *                                          SilcChannelEntry channel,
- *                                          SilcChannelPrivateKey key,
- *                                          SilcMessageFlags flags,
- *                                          unsigned char *data,
- *                                          SilcUInt32 data_len,
- *                                          SilcBool force_send);
+ *                                              SilcClientConnection conn,
+ *                                              SilcChannelEntry channel,
+ *                                              SilcChannelPrivateKey key,
+ *                                              SilcMessageFlags flags,
+ *                                              unsigned char *data,
+ *                                              SilcUInt32 data_len);
  *
  * DESCRIPTION
  *
@@ -1180,8 +1131,7 @@ void silc_client_close_connection(SilcClient client,
  *    differently from "normal" packets. SILC header of the packet is
  *    encrypted with the next receiver's key and the rest of the packet is
  *    encrypted with the channel specific key. Padding and HMAC is computed
- *    with the next receiver's key. The `data' is the channel message. If
- *    the `force_send' is TRUE then the packet is sent immediately.
+ *    with the next receiver's key. The `data' is the channel message.
  *
  *    If `key' is provided then that private key is used to encrypt the
  *    channel message.  If it is not provided, private keys has not been
@@ -1194,341 +1144,57 @@ void silc_client_close_connection(SilcClient client,
  *
  *    Returns TRUE if the message was sent, and FALSE if error occurred or
  *    the sending is not allowed due to channel modes (like sending is
- *    blocked).
+ *    blocked).  This function is thread safe and private messages can be
+ *    sent from multiple threads.
  *
  ***/
 SilcBool silc_client_send_channel_message(SilcClient client,
-                                     SilcClientConnection conn,
-                                     SilcChannelEntry channel,
-                                     SilcChannelPrivateKey key,
-                                     SilcMessageFlags flags,
-                                     unsigned char *data,
-                                     SilcUInt32 data_len,
-                                     SilcBool force_send);
+                                         SilcClientConnection conn,
+                                         SilcChannelEntry channel,
+                                         SilcChannelPrivateKey key,
+                                         SilcMessageFlags flags,
+                                         unsigned char *data,
+                                         SilcUInt32 data_len);
+
+/* Block process until channel message from `channel' is received */
+SilcBool
+silc_client_receive_channel_message(SilcClient client,
+                                   SilcClientConnection conn,
+                                   SilcChannelEntry channel,
+                                   SilcClientEntry *return_sender,
+                                   SilcMessageFlags *return_flags,
+                                   const unsigned char **return_message,
+                                   SilcUInt32 *return_message_len);
 
 /****f* silcclient/SilcClientAPI/silc_client_send_private_message
  *
  * SYNOPSIS
  *
  *    SilcBool silc_client_send_private_message(SilcClient client,
- *                                          SilcClientConnection conn,
- *                                          SilcClientEntry client_entry,
- *                                          SilcMessageFlags flags,
- *                                          unsigned char *data,
- *                                          SilcUInt32 data_len,
- *                                          SilcBool force_send);
+ *                                              SilcClientConnection conn,
+ *                                              SilcClientEntry client_entry,
+ *                                              SilcMessageFlags flags,
+ *                                              unsigned char *data,
+ *                                              SilcUInt32 data_len);
  *
  * DESCRIPTION
  *
  *    Sends private message to remote client. If private message key has
  *    not been set with this client then the message will be encrypted using
- *    normal session keys. Private messages are special packets in SILC
- *    network hence we need this own function for them. This is similar
- *    to silc_client_packet_send_to_channel except that we send private
- *    message. The `data' is the private message. If the `force_send' is
- *    TRUE the packet is sent immediately.
- *
- *    If the `flags' includes SILC_MESSAGE_FLAG_SIGNED the message will be
- *    digitally signed with the SILC key pair.
+ *    normal session keys.  If the `flags' includes SILC_MESSAGE_FLAG_SIGNED
+ *    the message will be digitally signed with the SILC key pair.
  *
  *    Returns TRUE if the message was sent, and FALSE if error occurred.
+ *    This function is thread safe and private messages can be sent from
+ *    multiple threads.
  *
  ***/
 SilcBool silc_client_send_private_message(SilcClient client,
-                                     SilcClientConnection conn,
-                                     SilcClientEntry client_entry,
-                                     SilcMessageFlags flags,
-                                     unsigned char *data,
-                                     SilcUInt32 data_len,
-                                     SilcBool force_send);
-
-
-/* Client and Channel entry retrieval (idlist.c) */
-
-/****f* silcclient/SilcClientAPI/SilcGetClientCallback
- *
- * SYNOPSIS
- *
- *    typedef void (*SilcGetClientCallback)(SilcClient client,
- *                                          SilcClientConnection conn,
- *                                          SilcClientEntry *clients,
- *                                          SilcUInt32 clients_count,
- *                                          void *context);
- *
- * DESCRIPTION
- *
- *    Callback function given to the silc_client_get_client function. The
- *    found entries are allocated into the `clients' array. The array must
- *    not be freed by the receiver, the library will free it later. If the
- *    `clients' is NULL, no such clients exist in the SILC Network.
- *
- ***/
-typedef void (*SilcGetClientCallback)(SilcClient client,
-                                     SilcClientConnection conn,
-                                     SilcClientEntry *clients,
-                                     SilcUInt32 clients_count,
-                                     void *context);
-
-/****f* silcclient/SilcClientAPI/silc_client_get_clients
- *
- * SYNOPSIS
- *
- *    void silc_client_get_clients(SilcClient client,
- *                                 SilcClientConnection conn,
- *                                 const char *nickname,
- *                                 const char *server,
- *                                 SilcGetClientCallback completion,
- *                                 void *context);
- *
- * DESCRIPTION
- *
- *    Finds client entry or entries by the `nickname' and `server'. The
- *    completion callback will be called when the client entries has been
- *    found.  After the server returns the client information it is cached
- *    and can be accesses locally at a later time.  The resolving is done
- *    with IDENTIFY command.  The `server' may be NULL.
- *
- * NOTES
- *
- *    NOTE: This function is always asynchronous and resolves the client
- *    information from the server. Thus, if you already know the client
- *    information then use the silc_client_get_client_by_id function to
- *    get the client entry since this function may be very slow and should
- *    be used only to initially get the client entries.
- *
- *    Since this routine resolves with IDENTIFY command only the relevant
- *    information (user's nickname and username) is resolved.  For example,
- *    user's real name, channel list and others are not resolved.  Caller
- *    can/must resolve those separately if they are needed (for example,
- *    with silc_client_get_client_by_id_resolve).
- *
- ***/
-void silc_client_get_clients(SilcClient client,
-                            SilcClientConnection conn,
-                            const char *nickname,
-                            const char *server,
-                            SilcGetClientCallback completion,
-                            void *context);
-
-/****f* silcclient/SilcClientAPI/silc_client_get_clients_whois
- *
- * SYNOPSIS
- *
- *    void silc_client_get_clients_whois(SilcClient client,
- *                                       SilcClientConnection conn,
- *                                       const char *nickname,
- *                                       const char *server,
- *                                       SilcBuffer attributes,
- *                                       SilcGetClientCallback completion,
- *                                       void *context);
- *
- * DESCRIPTION
- *
- *    Finds client entry or entries by the `nickname' and `server'. The
- *    completion callback will be called when the client entries has been
- *    found.  After the server returns the client information it is cached
- *    and can be accesses locally at a later time.  The resolving is done
- *    with WHOIS command.  The `server' may be NULL.
- *
- *    If the `attributes' is non-NULL then the buffer includes Requested
- *    Attributes which can be used to fetch very detailed information
- *    about the user. If it is NULL then only normal WHOIS query is
- *    made (for more information about attributes see SilcAttribute).
- *    Caller may create the `attributes' with silc_client_attributes_request
- *    function.
- *
- * NOTES
- *
- *    The resolving is done with WHOIS command. For this reason this
- *    command may take a long time because it resolves detailed user
- *    information.
- *
- ***/
-void silc_client_get_clients_whois(SilcClient client,
-                                  SilcClientConnection conn,
-                                  const char *nickname,
-                                  const char *server,
-                                  SilcBuffer attributes,
-                                  SilcGetClientCallback completion,
-                                  void *context);
-
-/****f* silcclient/SilcClientAPI/silc_client_get_clients_local
- *
- * SYNOPSIS
- *
- *    SilcClientEntry *silc_client_get_clients_local(SilcClient client,
- *                                                   SilcClientConnection conn,
- *                                                   const char *nickname,
- *                                                   const char *format,
- *                                                   SilcUInt32 *clients_count);
- *
- * DESCRIPTION
- *
- *    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 caller must return the
- *    returned array.
- *
- ***/
-SilcClientEntry *silc_client_get_clients_local(SilcClient client,
-                                              SilcClientConnection conn,
-                                              const char *nickname,
-                                              const char *format,
-                                              SilcUInt32 *clients_count);
-
-/****f* silcclient/SilcClientAPI/silc_client_get_clients_by_channel
- *
- * SYNOPSIS
- *
- *    void silc_client_get_clients_by_channel(SilcClient client,
- *                                            SilcClientConnection conn,
- *                                            SilcChannelEntry channel,
- *                                            SilcGetClientCallback completion,
- *                                            void *context);
- *
- * DESCRIPTION
- *
- *    Gets client entries by the channel indicated by `channel'. Thus,
- *    it resovles the users currently on that channel. If all users are
- *    already resolved this returns the users from the channel. If the
- *    users are resolved only partially this resolves the complete user
- *    information. If no users are resolved on this channel at all, this
- *    calls USERS command to resolve all users on the channel. The `completion'
- *    will be called after the entries are available. When server returns
- *    the client information it will be cached and can be accessed locally
- *    at a later time.
- *
- *    This function can be used for example in SILC_COMMAND_JOIN command
- *    reply handling in application to resolve users on that channel.  It
- *    also can be used after calling silc_client_get_channel_resolve to
- *    resolve users on that channel.
- *
- * NOTES
- *
- *    The resolving is done with WHOIS command. For this reason this
- *    command may take a long time because it resolves detailed user
- *    information.
- *
- ***/
-void silc_client_get_clients_by_channel(SilcClient client,
-                                       SilcClientConnection conn,
-                                       SilcChannelEntry channel,
-                                       SilcGetClientCallback completion,
-                                       void *context);
-
-/****f* silcclient/SilcClientAPI/silc_client_get_clients_by_list
- *
- * SYNOPSIS
- *
- *    void silc_client_get_clients_by_list(SilcClient client,
- *                                         SilcClientConnection conn,
- *                                         SilcUInt32 list_count,
- *                                         SilcBuffer client_id_list,
- *                                         SilcGetClientCallback completion,
- *                                         void *context);
- *
- * DESCRIPTION
- *
- *    Gets client entries by the list of client ID's `client_id_list'. This
- *    always resolves those client ID's it does not know yet from the server
- *    so this function might take a while. The `client_id_list' is a list
- *    of ID Payloads added one after other.  JOIN command reply and USERS
- *    command reply for example returns this sort of list. The `completion'
- *    will be called after the entries are available. When server returns
- *    the client information it will be cached and can be accessed locally
- *    at a later time.
- *
- * NOTES
- *
- *    The resolving is done with IDENTIFY command. This means that only
- *    the relevant information of user (it's nickname and username) is
- *    resolved. For example, user's real name, channel lists and others
- *    are not resolved. Caller can/must resolve those separately if they
- *    are needed (for example, with silc_client_get_client_by_id_resolve).
- *
- ***/
-void silc_client_get_clients_by_list(SilcClient client,
-                                    SilcClientConnection conn,
-                                    SilcUInt32 list_count,
-                                    SilcBuffer client_id_list,
-                                    SilcGetClientCallback completion,
-                                    void *context);
-
-/****f* silcclient/SilcClientAPI/silc_client_get_client_by_id
- *
- * SYNOPSIS
- *
- *    SilcClientEntry silc_client_get_client_by_id(SilcClient client,
- *                                                 SilcClientConnection conn,
- *                                                 SilcClientID *client_id);
- *
- * DESCRIPTION
- *
- *    Find entry for client by the client's ID. Returns the entry or NULL
- *    if the entry was not found.  This checks the local cache and does
- *    not resolve anything from server.
- *
- ***/
-SilcClientEntry silc_client_get_client_by_id(SilcClient client,
-                                            SilcClientConnection conn,
-                                            SilcClientID *client_id);
-
-/****f* silcclient/SilcClientAPI/silc_client_get_client_by_id_resolve
- *
- * SYNOPSIS
- *
- *    void
- *    silc_client_get_client_by_id_resolve(SilcClient client,
- *                                         SilcClientConnection conn,
- *                                         SilcClientID *client_id,
- *                                         SilcBuffer attributes,
- *                                         SilcGetClientCallback completion,
- *                                         void *context);
- *
- * DESCRIPTION
- *
- *    Same as silc_client_get_client_by_id but will always resolve the
- *    information from the server. Use this only if you know that you
- *    do not have the entry and the only thing you know about the client
- *    is its ID. When server returns the client information it will be
- *    cache and can be accessed locally at a later time. The resolving
- *    is done by sending WHOIS command.
- *
- *    If the `attributes' is non-NULL then the buffer includes Requested
- *    Attributes which can be used to fetch very detailed information
- *    about the user. If it is NULL then only normal WHOIS query is
- *    made (for more information about attributes see SilcAttribute).
- *    Caller may create the `attributes' with silc_client_attributes_request
- *    function.
- *
- ***/
-void silc_client_get_client_by_id_resolve(SilcClient client,
                                          SilcClientConnection conn,
-                                         SilcClientID *client_id,
-                                         SilcBuffer attributes,
-                                         SilcGetClientCallback completion,
-                                         void *context);
-
-/****f* silcclient/SilcClientAPI/silc_client_del_client
- *
- * SYNOPSIS
- *
- *    SilcBool silc_client_del_client(SilcClient client, SilcClientConnection conn,
- *                                SilcClientEntry client_entry)
- *
- * DESCRIPTION
- *
- *    Removes client from local cache by the client entry indicated by
- *    the `client_entry'.  Returns TRUE if the deletion were successful.
- *
- ***/
-SilcBool silc_client_del_client(SilcClient client, SilcClientConnection conn,
-                           SilcClientEntry client_entry);
+                                         SilcClientEntry client_entry,
+                                         SilcMessageFlags flags,
+                                         unsigned char *data,
+                                         SilcUInt32 data_len);
 
 /****f* silcclient/SilcClientAPI/SilcGetChannelCallback
  *
@@ -1662,7 +1328,7 @@ void silc_client_get_channel_by_id_resolve(SilcClient client,
  *
  ***/
 SilcBool silc_client_del_channel(SilcClient client, SilcClientConnection conn,
-                            SilcChannelEntry channel);
+                                SilcChannelEntry channel);
 
 /****f* silcclient/SilcClientAPI/silc_client_get_server
  *
@@ -1714,7 +1380,7 @@ SilcServerEntry silc_client_get_server_by_id(SilcClient client,
  *
  ***/
 SilcBool silc_client_del_server(SilcClient client, SilcClientConnection conn,
-                           SilcServerEntry server);
+                               SilcServerEntry server);
 
 /****f* silcclient/SilcClientAPI/silc_client_on_channel
  *
@@ -1733,15 +1399,16 @@ SilcBool silc_client_del_server(SilcClient client, SilcClientConnection conn,
 SilcChannelUser silc_client_on_channel(SilcChannelEntry channel,
                                       SilcClientEntry client_entry);
 
-/* Command management (command.c) */
+
+/* Command management */
 
 /****f* silcclient/SilcClientAPI/silc_client_command_call
  *
  * SYNOPSIS
  *
- *    SilcBool silc_client_command_call(SilcClient client,
- *                                  SilcClientConnection conn,
- *                                  const char *command_line, ...);
+ *    SilcUInt16 silc_client_command_call(SilcClient client,
+ *                                        SilcClientConnection conn,
+ *                                        const char *command_line, ...);
  *
  * DESCRIPTION
  *
@@ -1781,45 +1448,69 @@ SilcChannelUser silc_client_on_channel(SilcChannelEntry channel,
  *    function instead.
  *
  ***/
-SilcBool silc_client_command_call(SilcClient client,
-                             SilcClientConnection conn,
-                             const char *command_line, ...);
+SilcUInt16 silc_client_command_call(SilcClient client,
+                                   SilcClientConnection conn,
+                                   const char *command_line, ...);
+
+/* If FALSE is returned the callback will not be called again, even if there
+   is more data coming in in the command reply. */
+typedef SilcBool (*SilcClientCommandReply)(SilcClient client,
+                                          SilcClientConnection conn,
+                                          SilcCommand command,
+                                          SilcStatus status,
+                                          SilcStatus error,
+                                          void *context,
+                                          va_list ap);
 
 /****f* silcclient/SilcClientAPI/silc_client_command_send
  *
  * SYNOPSIS
  *
- *    void silc_client_command_send(SilcClient client,
- *                                  SilcClientConnection conn,
- *                                  SilcCommand command, SilcUInt16 ident,
- *                                  SilcUInt32 argc, ...);
+ *    SilcUInt16 silc_client_command_send(SilcClient client,
+ *                                        SilcClientConnection conn,
+ *                                        SilcCommand command,
+ *                                        SilcClientCommandReply reply,
+ *                                        void *reply_context,
+ *                                        SilcUInt32 argc, ...);
  *
  * DESCRIPTION
  *
- *    Generic function to send any command. The arguments must be sent already
- *    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
+ *    Generic function to send any command.  The arguments must be given
+ *    already encoded into correct format 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.
- *    Note that this overriders the Client Librarys commands and sends
- *    the command packet directly to server.
- *
  *    Programmer should get familiar with the SILC protocol commands
  *    specification when using this function, as the arguments needs to
  *    be encoded as specified in the protocol.
  *
- *    The variable arguments are a pair of { type, data, data_length },
- *    and the `argc' is the number of these pairs.
+ *    The variable arguments are a set of { type, data, data_length },
+ *    and the `argc' is the number of these sets.
+ *
+ *    The `reply' callback must be provided, and it is called when the
+ *    command reply is received from the server.  Note that, when using this
+ *    function the default `command_reply' client operation will not be
+ *    called, when reply is received.  Note however that, `reply' is
+ *    identical with `command_reply' callback, and application may forward
+ *    the reply from `reply' to `command_reply' callback, if desired.
+ *
+ *    Returns command identifier for this sent command.  It can be used
+ *    to additionally attach to the command reply using the function
+ *    silc_client_command_pending, if needed.  Returns 0 on error,
  *
  * EXAMPLE
  *
- *    silc_client_command_send(client, conn, SILC_COMMAND_WHOIS, 0, 1,
- *                             1, nickname, strlen(nickname));
+ *    silc_client_command_send(client, conn, SILC_COMMAND_WHOIS,
+ *                             my_whois_command_reply, cmd_ctx,
+ *                             1, 1, nickname, strlen(nickname));
  *
  ***/
-void silc_client_command_send(SilcClient client, SilcClientConnection conn,
-                             SilcCommand command, SilcUInt16 ident,
-                             SilcUInt32 argc, ...);
+SilcUInt16 silc_client_command_send(SilcClient client,
+                                   SilcClientConnection conn,
+                                   SilcCommand command,
+                                   SilcClientCommandReply reply,
+                                   void *reply_context,
+                                   SilcUInt32 argc, ...);
 
 /****f* silcclient/SilcClientAPI/silc_client_command_pending
  *
@@ -1827,7 +1518,7 @@ void silc_client_command_send(SilcClient client, SilcClientConnection conn,
  *
  *    void silc_client_command_pending(SilcClientConnection conn,
  *                                     SilcCommand reply_cmd,
- *                                     SilcUInt16 ident,
+ *                                     SilcUInt16 cmd-ident,
  *                                     SilcCommandCb callback,
  *                                     void *context);
  *
@@ -1841,7 +1532,7 @@ void silc_client_command_send(SilcClient client, SilcClientConnection conn,
  *    SilcClientCommandReplyContext which includes the internals of the
  *    command reply.
  *
- *    The `ident' is a command identifier which was set for the earlier
+ *    The `cmd_ident' is a command identifier which was set for the earlier
  *    sent command.  The command reply will include the same identifier
  *    and pending command callback will be called when the reply is
  *    received with the same command identifier.  It is possible to
@@ -1872,11 +1563,11 @@ void silc_client_command_send(SilcClient client, SilcClientConnection conn,
  *                                my_ping_handler, my_ping_context);
  *
  ***/
-void silc_client_command_pending(SilcClientConnection conn,
-                                SilcCommand reply_cmd,
-                                SilcUInt16 ident,
-                                SilcCommandCb callback,
-                                void *context);
+SilcBool silc_client_command_pending(SilcClientConnection conn,
+                                    SilcCommand command,
+                                    SilcUInt16 cmd_ident,
+                                    SilcClientCommandReply reply,
+                                    void *context);
 
 
 /* Private Message key management (client_prvmsg.c) */
@@ -1885,15 +1576,16 @@ void silc_client_command_pending(SilcClientConnection conn,
  *
  * SYNOPSIS
  *
- *    SilcBool silc_client_add_private_message_key(SilcClient client,
- *                                             SilcClientConnection conn,
- *                                             SilcClientEntry client_entry,
- *                                             const char *cipher,
- *                                             const char *hmac,
- *                                             unsigned char *key,
- *                                             SilcUInt32 key_len,
- *                                             SilcBool generate_key,
- *                                             SilcBool responder);
+ *    SilcBool
+ *    silc_client_add_private_message_key(SilcClient client,
+ *                                        SilcClientConnection conn,
+ *                                        SilcClientEntry client_entry,
+ *                                        const char *cipher,
+ *                                        const char *hmac,
+ *                                        unsigned char *key,
+ *                                        SilcUInt32 key_len,
+ *                                        SilcBool generate_key,
+ *                                        SilcBool responder);
  *
  * DESCRIPTION
  *
@@ -1920,14 +1612,14 @@ void silc_client_command_pending(SilcClientConnection conn,
  *
  ***/
 SilcBool silc_client_add_private_message_key(SilcClient client,
-                                        SilcClientConnection conn,
-                                        SilcClientEntry client_entry,
-                                        const char *cipher,
-                                        const char *hmac,
-                                        unsigned char *key,
-                                        SilcUInt32 key_len,
-                                        SilcBool generate_key,
-                                        SilcBool responder);
+                                            SilcClientConnection conn,
+                                            SilcClientEntry client_entry,
+                                            const char *cipher,
+                                            const char *hmac,
+                                            unsigned char *key,
+                                            SilcUInt32 key_len,
+                                            SilcBool generate_key,
+                                            SilcBool responder);
 
 /****f* silcclient/SilcClientAPI/silc_client_add_private_message_key_ske
  *
@@ -1939,7 +1631,7 @@ SilcBool silc_client_add_private_message_key(SilcClient client,
  *                                            SilcClientEntry client_entry,
  *                                            const char *cipher,
  *                                            const char *hmac,
- *                                            SilcSKEKeyMaterial *key);
+ *                                            SilcSKEKeyMaterial key);
  *
  * DESCRIPTION
  *
@@ -1951,12 +1643,12 @@ SilcBool silc_client_add_private_message_key(SilcClient client,
  *
  ***/
 SilcBool silc_client_add_private_message_key_ske(SilcClient client,
-                                            SilcClientConnection conn,
-                                            SilcClientEntry client_entry,
-                                            const char *cipher,
-                                            const char *hmac,
-                                            SilcSKEKeyMaterial *key,
-                                            SilcBool responder);
+                                                SilcClientConnection conn,
+                                                SilcClientEntry client_entry,
+                                                const char *cipher,
+                                                const char *hmac,
+                                                SilcSKEKeyMaterial key,
+                                                SilcBool responder);
 
 /****f* silcclient/SilcClientAPI/silc_client_del_private_message_key
  *
@@ -1974,8 +1666,8 @@ SilcBool silc_client_add_private_message_key_ske(SilcClient client,
  *
  ***/
 SilcBool silc_client_del_private_message_key(SilcClient client,
-                                        SilcClientConnection conn,
-                                        SilcClientEntry client_entry);
+                                            SilcClientConnection conn,
+                                            SilcClientEntry client_entry);
 
 /****f* silcclient/SilcClientAPI/silc_client_list_private_message_keys
  *
@@ -2106,14 +1798,14 @@ void silc_client_free_private_message_keys(SilcPrivateMessageKeys keys,
  *
  ***/
 SilcBool silc_client_add_channel_private_key(SilcClient client,
-                                        SilcClientConnection conn,
-                                        SilcChannelEntry channel,
-                                        const char *name,
-                                        char *cipher,
-                                        char *hmac,
-                                        unsigned char *key,
-                                        SilcUInt32 key_len,
-                                        SilcChannelPrivateKey *ret_key);
+                                            SilcClientConnection conn,
+                                            SilcChannelEntry channel,
+                                            const char *name,
+                                            char *cipher,
+                                            char *hmac,
+                                            unsigned char *key,
+                                            SilcUInt32 key_len,
+                                            SilcChannelPrivateKey *ret_key);
 
 /****f* silcclient/SilcClientAPI/silc_client_del_channel_private_keys
  *
@@ -2131,8 +1823,8 @@ SilcBool silc_client_add_channel_private_key(SilcClient client,
  *
  ***/
 SilcBool silc_client_del_channel_private_keys(SilcClient client,
-                                         SilcClientConnection conn,
-                                         SilcChannelEntry channel);
+                                             SilcClientConnection conn,
+                                             SilcChannelEntry channel);
 
 /****f* silcclient/SilcClientAPI/silc_client_del_channel_private_key
  *
@@ -2154,9 +1846,9 @@ SilcBool silc_client_del_channel_private_keys(SilcClient client,
  *
  ***/
 SilcBool silc_client_del_channel_private_key(SilcClient client,
-                                        SilcClientConnection conn,
-                                        SilcChannelEntry channel,
-                                        SilcChannelPrivateKey key);
+                                            SilcClientConnection conn,
+                                            SilcChannelEntry channel,
+                                            SilcChannelPrivateKey key);
 
 /****f* silcclient/SilcClientAPI/silc_client_list_channel_private_keys
  *
@@ -2832,39 +2524,6 @@ SilcHashTable silc_client_attributes_get(SilcClient client,
  ***/
 SilcBuffer silc_client_attributes_request(SilcAttribute attribute, ...);
 
-/* Low level packet sending functions */
-
-/****f* silcclient/SilcClientAPI/silc_client_send_packet
- *
- * SYNOPSIS
- *
- *     SilcBool silc_client_send_packet(SilcClient client,
- *                                  SilcClientConnection conn,
- *                                  SilcPacketType type,
- *                                  const unsigned char *data,
- *                                  SilcUInt32 data_len);
- *
- * DESCRIPTION
- *
- *    This routine can be used by application to send packets directly
- *    to a connection indicated by `conn'.  Usually application does not
- *    need this routine since the Client Library handles the packet
- *    sending.  The `type' indicates the packet type.  If `data' is
- *    NULL then empty packet is sent.  This returns FALSE if packet cannot
- *    be sent.
- *
- ***/
-SilcBool silc_client_send_packet(SilcClient client,
-                            SilcClientConnection conn,
-                            SilcPacketType type,
-                            const unsigned char *data,
-                            SilcUInt32 data_len);
-
-#include "command.h"
-#include "command_reply.h"
-#include "idlist.h"
-#include "protocol.h"
-
 #ifdef __cplusplus
 }
 #endif
diff --git a/lib/silcclient/silcclient_entry.h b/lib/silcclient/silcclient_entry.h
new file mode 100644 (file)
index 0000000..abc078b
--- /dev/null
@@ -0,0 +1,519 @@
+/*
+
+  silcclient_entry.h
+
+  Author: Pekka Riikonen <priikone@silcnet.org>
+
+  Copyright (C) 2006 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; 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.
+
+*/
+
+#ifndef SILCCLIENT_ENTRY_H
+#define SILCCLIENT_ENTRY_H
+
+#ifndef SILCCLIENT_H
+#error "Do not include this header directly, include silcclient.h instead"
+#endif
+
+/****s* silcclient/SilcClientAPI/SilcClientEntry
+ *
+ * NAME
+ *
+ *    typedef struct SilcClientEntryStruct { ... } *SilcClientEntry;
+ *
+ * DESCRIPTION
+ *
+ *    This structure represents a client or a user in the SILC network.
+ *    The local user has this structure also and it can be accessed from
+ *    SilcClientConnection structure.  All other users in the SILC network
+ *    that are accessed using the Client Library routines will have their
+ *    own SilcClientEntry structure.  For example, when finding users by
+ *    their nickname the Client Library returns this structure back to
+ *    the application.  All strings in the structure are UTF-8 encoded.
+ *
+ *    Application may store its own pointer into the context pointer in
+ *    this structure.
+ *
+ * NOTES
+ *
+ *    If application wants to store nickname or any of the other strings
+ *    it should always duplicated them.
+ *
+ *    None of the string arrays are set if the first character is '\0'.
+ *    All string arrays are always NULL terminated.
+ *
+ *    If application stores the SilcClientEntry it must always take
+ *    a reference of it by calling silc_client_ref_client function.  The
+ *    reference must be released after it is not needed anymore by calling
+ *    silc_client_unref_client function.
+ *
+ * SOURCE
+ */
+struct SilcClientEntryStruct {
+  char nickname[128 + 1];           /* Nickname */
+  char username[128 + 1];           /* Username */
+  char hostname[256 + 1];           /* Hostname */
+  char server  [256 + 1];           /* SILC server name */
+  char *realname;                   /* Realname (userinfo) */
+  char *nickname_normalized;        /* Normalized nickname */
+
+  SilcClientID id;                  /* The Client ID */
+  SilcUInt32 mode;                  /* User mode in SILC, see SilcUserMode */
+  SilcPublicKey public_key;         /* User's public key, may be NULL */
+  SilcHashTable channels;           /* Channels client has joined */
+  SilcDList attrs;                  /* Requested Attributes (maybe NULL) */
+  unsigned char fingerprint[20];     /* SHA-1 fingerprint of the public key */
+
+  void *context;                    /* Application specific context */
+  SilcClientEntryInternal internal;
+};
+/***/
+
+/****s* silcclient/SilcClientAPI/SilcChannelEntry
+ *
+ * NAME
+ *
+ *    typedef struct SilcChannelEntryStruct { ... } *SilcChannelEntry;
+ *
+ * DESCRIPTION
+ *
+ *    This structure represents a channel in the SILC network.  All
+ *    channels that the client are aware of or have joined in will be
+ *    represented as SilcChannelEntry.  The structure includes information
+ *    about the channel.  All strings in the structure are UTF-8 encoded.
+ *
+ * SOURCE
+ */
+struct SilcChannelEntryStruct {
+  /* General information */
+  char *channel_name;                       /* Channel name */
+  SilcChannelID *id;                        /* Channel ID */
+  SilcUInt32 mode;                          /* Channel mode, ChannelModes. */
+  char *topic;                              /* Current topic, may be NULL */
+  SilcPublicKey founder_key;                /* Founder key, may be NULL */
+  SilcUInt32 user_limit;                    /* User limit on channel */
+
+  /* All clients that has joined this channel.  The key to the table is the
+     SilcClientEntry and the context is SilcChannelUser context. */
+  SilcHashTable user_list;
+
+  /* Channel keys */
+  SilcCipher channel_key;                    /* The channel key */
+  unsigned char *key;                       /* Raw key data */
+  SilcUInt32 key_len;                       /* Raw key data length */
+  unsigned char iv[SILC_CIPHER_MAX_IV_SIZE]; /* Current IV */
+  SilcHmac hmac;                            /* Current HMAC */
+
+  /* Channel private keys */
+  SilcDList private_keys;                   /* List of private keys or NULL */
+  SilcChannelPrivateKey curr_key;           /* Current private key */
+
+  /* SilcChannelEntry status information */
+  SilcDList old_channel_keys;
+  SilcDList old_hmacs;
+  SilcUInt16 resolve_cmd_ident;                     /* Command identifier when
+                                               resolving this entry */
+
+  /* Application specific data.  Application may set here whatever it wants. */
+  void *context;
+};
+/***/
+
+/****s* silcclient/SilcClientAPI/SilcServerEntry
+ *
+ * NAME
+ *
+ *    typedef struct SilcServerEntryStruct { ... } *SilcServerEntry
+ *
+ * DESCRIPTION
+ *
+ *    This structure represents a server in the SILC network.  All servers
+ *    that the client is aware of and have for example resolved with
+ *    SILC_COMMAND_INFO command have their on SilcServerEntry structure.
+ *    All strings in the structure are UTF-8 encoded.
+ *
+ * SOURCE
+ */
+struct SilcServerEntryStruct {
+  /* General information */
+  char *server_name;                        /* Server name */
+  char *server_info;                        /* Server info */
+  SilcServerID *server_id;                  /* Server ID */
+  SilcUInt16 resolve_cmd_ident;                     /* Command identifier when
+                                               resolving this entry */
+
+  /* Application specific data.  Application may set here whatever it wants. */
+  void *context;
+};
+/***/
+
+/* SilcClientEntry routines */
+
+/****f* silcclient/SilcClientAPI/SilcGetClientCallback
+ *
+ * SYNOPSIS
+ *
+ *    typedef void (*SilcGetClientCallback)(SilcClient client,
+ *                                          SilcClientConnection conn,
+ *                                          SilcStatus status,
+ *                                          SilcDList clients,
+ *                                          void *context);
+ *
+ * DESCRIPTION
+ *
+ *    Callback function given to various client search functions.  The
+ *    found entries are allocated into the `clients' list.  The list must
+ *    not be freed by the receiver, the library will free it later.  If the
+ *    `clients' is NULL, no such clients exist in the SILC Network, and
+ *    the `status' will include the error.
+ *
+ * NOTES
+ *
+ *    If the application stores any of the SilcClientEntry pointers from
+ *    the `clients' list it must reference it with silc_client_ref_client
+ *    function.
+ *
+ *    Application must not free the returned `clients' list.
+ *
+ ***/
+typedef void (*SilcGetClientCallback)(SilcClient client,
+                                     SilcClientConnection conn,
+                                     SilcStatus status,
+                                     SilcDList clients,
+                                     void *context);
+
+/****f* silcclient/SilcClientAPI/silc_client_ref_client
+ *
+ * SYNOPSIS
+ *
+ *    void silc_client_ref_client(SilcClient client,
+ *                                SilcClientConnection conn,
+ *                                SilcClientEntry client_entry);
+ *
+ * DESCRIPTION
+ *
+ *    Takes a reference of the client entry indicated by `client_entry'
+ *    The reference must be released by calling silc_client_unref_client
+ *    after it is not needed anymore.
+ *
+ ***/
+void silc_client_ref_client(SilcClient client, SilcClientConnection conn,
+                           SilcClientEntry client_entry);
+
+/****f* silcclient/SilcClientAPI/silc_client_unref_client
+ *
+ * SYNOPSIS
+ *
+ *    void silc_client_unref_client(SilcClient client,
+ *                                  SilcClientConnection conn,
+ *                                  SilcClientEntry client_entry);
+ *
+ * DESCRIPTION
+ *
+ *    Releases the client entry reference indicated by `client_entry'.
+ *
+ ***/
+void silc_client_unref_client(SilcClient client, SilcClientConnection conn,
+                             SilcClientEntry client_entry);
+
+/****f* silcclient/SilcClientAPI/silc_client_list_free
+ *
+ * SYNOPSIS
+ *
+ *    void silc_client_list_free(SilcClient client,
+ *                               SilcClientConnection conn,
+ *                               SilcDList client_list);
+ *
+ * DESCRIPTION
+ *
+ *    Free's client entry list that has been returned by various library
+ *    routines.
+ *
+ ***/
+void silc_client_list_free(SilcClient client, SilcClientConnection conn,
+                          SilcDList client_list);
+
+/****f* silcclient/SilcClientAPI/silc_client_get_clients
+ *
+ * SYNOPSIS
+ *
+ *    SilcUInt16 silc_client_get_clients(SilcClient client,
+ *                                       SilcClientConnection conn,
+ *                                       const char *nickname,
+ *                                       const char *server,
+ *                                       SilcGetClientCallback completion,
+ *                                       void *context);
+ *
+ * DESCRIPTION
+ *
+ *    Finds client entry or entries by the `nickname' and `server'. The
+ *    completion callback will be called when the client entries has been
+ *    found.  After the server returns the client information it is cached
+ *    and can be accesses locally at a later time.  The resolving is done
+ *    with IDENTIFY command.  The `server' may be NULL.  Returns 0 on
+ *    error and the command identifier used with the command otherwise.
+ *
+ * NOTES
+ *
+ *    This function is always asynchronous and resolves the client
+ *    information from the server.  Thus, if you already know the client
+ *    information then use the silc_client_get_client_by_id function to
+ *    get the client entry since this function may be very slow and should
+ *    be used only to initially get the client entries.
+ *
+ *    This function resolves only the relevant information (user's nickname
+ *    and username).  It does not resolve for example user's real name,
+ *    joined channel list or other information.  To resolve all the details
+ *    use silc_client_get_clients_whois instead.
+ *
+ ***/
+SilcUInt16 silc_client_get_clients(SilcClient client,
+                                  SilcClientConnection conn,
+                                  const char *nickname,
+                                  const char *server,
+                                  SilcGetClientCallback completion,
+                                  void *context);
+
+/****f* silcclient/SilcClientAPI/silc_client_get_clients_whois
+ *
+ * SYNOPSIS
+ *
+ *    SilcUInt16
+ *    silc_client_get_clients_whois(SilcClient client,
+ *                                  SilcClientConnection conn,
+ *                                  const char *nickname,
+ *                                  const char *server,
+ *                                  SilcBuffer attributes,
+ *                                  SilcGetClientCallback completion,
+ *                                  void *context);
+ *
+ * DESCRIPTION
+ *
+ *    Finds client entry or entries by the `nickname' and `server'. The
+ *    completion callback will be called when the client entries has been
+ *    found.  After the server returns the client information it is cached
+ *    and can be accesses locally at a later time.  The resolving is done
+ *    with WHOIS command.  The `server' may be NULL.  Returns 0 on error,
+ *    and the command identifier used with the command otherwise.
+ *
+ *    If the `attributes' is non-NULL then the buffer includes Requested
+ *    Attributes which can be used to fetch very detailed information
+ *    about the user. If it is NULL then only normal WHOIS query is
+ *    made (for more information about attributes see SilcAttribute).
+ *    Caller may create the `attributes' with silc_client_attributes_request
+ *    function.
+ *
+ * NOTES
+ *
+ *    The resolving is done with WHOIS command.  For this reason this
+ *    command may take a long time because it resolves detailed user
+ *    information.
+ *
+ ***/
+SilcUInt16 silc_client_get_clients_whois(SilcClient client,
+                                        SilcClientConnection conn,
+                                        const char *nickname,
+                                        const char *server,
+                                        SilcBuffer attributes,
+                                        SilcGetClientCallback completion,
+                                        void *context);
+
+/****f* silcclient/SilcClientAPI/silc_client_get_clients_local
+ *
+ * SYNOPSIS
+ *
+ *    SilcDList silc_client_get_clients_local(SilcClient client,
+ *                                            SilcClientConnection conn,
+ *                                            const char *nickname,
+ *                                            const char *format);
+ *
+ * DESCRIPTION
+ *
+ *    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 caller must free the
+ *    returned list by silc_client_list_free function.
+ *
+ * NOTES
+ *
+ *    If the application stores any of the SilcClientEntry pointers from
+ *    the returned list it must reference it with silc_client_ref_client
+ *    function.
+ *
+ *    Application must free the returned list with silc_client_list_free
+ *    function.
+ *
+ ***/
+SilcDList silc_client_get_clients_local(SilcClient client,
+                                       SilcClientConnection conn,
+                                       const char *nickname,
+                                       const char *format);
+
+/****f* silcclient/SilcClientAPI/silc_client_get_clients_by_channel
+ *
+ * SYNOPSIS
+ *
+ *    void silc_client_get_clients_by_channel(SilcClient client,
+ *                                            SilcClientConnection conn,
+ *                                            SilcChannelEntry channel,
+ *                                            SilcGetClientCallback completion,
+ *                                            void *context);
+ *
+ * DESCRIPTION
+ *
+ *    Gets client entries by the channel indicated by `channel'. Thus,
+ *    it resovles the users currently on that channel. If all users are
+ *    already resolved this returns the users from the channel. If the
+ *    users are resolved only partially this resolves the complete user
+ *    information. If no users are resolved on this channel at all, this
+ *    calls USERS command to resolve all users on the channel. The `completion'
+ *    will be called after the entries are available. When server returns
+ *    the client information it will be cached and can be accessed locally
+ *    at a later time.
+ *
+ *    This function can be used for example in SILC_COMMAND_JOIN command
+ *    reply handling in application to resolve users on that channel.  It
+ *    also can be used after calling silc_client_get_channel_resolve to
+ *    resolve users on that channel.
+ *
+ * NOTES
+ *
+ *    The resolving is done with WHOIS command.  For this reason this
+ *    command may take a long time because it resolves detailed user
+ *    information.
+ *
+ ***/
+void silc_client_get_clients_by_channel(SilcClient client,
+                                       SilcClientConnection conn,
+                                       SilcChannelEntry channel,
+                                       SilcGetClientCallback completion,
+                                       void *context);
+
+/****f* silcclient/SilcClientAPI/silc_client_get_clients_by_list
+ *
+ * SYNOPSIS
+ *
+ *    void silc_client_get_clients_by_list(SilcClient client,
+ *                                         SilcClientConnection conn,
+ *                                         SilcUInt32 list_count,
+ *                                         SilcBuffer client_id_list,
+ *                                         SilcGetClientCallback completion,
+ *                                         void *context);
+ *
+ * DESCRIPTION
+ *
+ *    Gets client entries by the list of client ID's `client_id_list'. This
+ *    always resolves those client ID's it doesn't know about from the server.
+ *    The `client_id_list' is a list of ID Payloads added one after other.
+ *    JOIN command reply and USERS command reply for example returns this sort
+ *    of list. The `completion' will be called after the entries are available.
+ *    When server returns the client information it will be cached and can be
+ *    accessed locally at a later time.  The resolving is done with WHOIS
+ *    command.
+ *
+ * NOTES
+ *
+ *    If even after resolving some Client ID in the `client_id_list' is
+ *    unknown it will be ignored and error is not returned.
+ *
+ ***/
+void silc_client_get_clients_by_list(SilcClient client,
+                                    SilcClientConnection conn,
+                                    SilcUInt32 list_count,
+                                    SilcBuffer client_id_list,
+                                    SilcGetClientCallback completion,
+                                    void *context);
+
+/****f* silcclient/SilcClientAPI/silc_client_get_client_by_id
+ *
+ * SYNOPSIS
+ *
+ *    SilcClientEntry silc_client_get_client_by_id(SilcClient client,
+ *                                                 SilcClientConnection conn,
+ *                                                 SilcClientID *client_id);
+ *
+ * DESCRIPTION
+ *
+ *    Find client entry by the client's ID.  Returns the entry or NULL
+ *    if the entry was not found.  This checks the local cache and does
+ *    not resolve anything from server.
+ *
+ * NOTES
+ *
+ *    The returned SilcClientEntry has been referenced by the library and
+ *    the caller must call silc_client_unref_client after the entry is not
+ *    needed anymore.
+ *
+ ***/
+SilcClientEntry silc_client_get_client_by_id(SilcClient client,
+                                            SilcClientConnection conn,
+                                            SilcClientID *client_id);
+
+/****f* silcclient/SilcClientAPI/silc_client_get_client_by_id_resolve
+ *
+ * SYNOPSIS
+ *
+ *    void
+ *    silc_client_get_client_by_id_resolve(SilcClient client,
+ *                                         SilcClientConnection conn,
+ *                                         SilcClientID *client_id,
+ *                                         SilcBuffer attributes,
+ *                                         SilcGetClientCallback completion,
+ *                                         void *context);
+ *
+ * DESCRIPTION
+ *
+ *    Same as silc_client_get_client_by_id but will always resolve the
+ *    information from the server. Use this only if you know that you
+ *    do not have the entry and the only thing you know about the client
+ *    is its ID. When server returns the client information it will be
+ *    cache and can be accessed locally at a later time. The resolving
+ *    is done by sending WHOIS command.
+ *
+ *    If the `attributes' is non-NULL then the buffer includes Requested
+ *    Attributes which can be used to fetch very detailed information
+ *    about the user. If it is NULL then only normal WHOIS query is
+ *    made (for more information about attributes see SilcAttribute).
+ *    Caller may create the `attributes' with silc_client_attributes_request
+ *    function.
+ *
+ ***/
+void silc_client_get_client_by_id_resolve(SilcClient client,
+                                         SilcClientConnection conn,
+                                         SilcClientID *client_id,
+                                         SilcBuffer attributes,
+                                         SilcGetClientCallback completion,
+                                         void *context);
+
+/****f* silcclient/SilcClientAPI/silc_client_del_client
+ *
+ * SYNOPSIS
+ *
+ *    SilcBool silc_client_del_client(SilcClient client, SilcClientConnection conn,
+ *                                SilcClientEntry client_entry)
+ *
+ * DESCRIPTION
+ *
+ *    Removes client from local cache by the client entry indicated by
+ *    the `client_entry'.  Returns TRUE if the deletion were successful.
+ *
+ ***/
+SilcBool silc_client_del_client(SilcClient client, SilcClientConnection conn,
+                               SilcClientEntry client_entry);
+
+
+#endif /* SILCCLIENT_ENTRY_H */
diff --git a/lib/silcclient/tests/Makefile.am b/lib/silcclient/tests/Makefile.am
new file mode 100644 (file)
index 0000000..34382a4
--- /dev/null
@@ -0,0 +1,27 @@
+#
+#  Makefile.am
+#
+#  Author: Pekka Riikonen <priikone@silcnet.org>
+#
+#  Copyright (C) 2006 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; 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.
+#
+
+AUTOMAKE_OPTIONS = 1.0 no-dependencies foreign
+
+bin_PROGRAMS =         test_silcclient
+
+test_silcclient_SOURCES = test_silcclient.c
+
+LIBS = $(SILC_COMMON_LIBS)
+LDADD = -L.. -L../.. -lsilc -lsilcclient
+
+include $(top_srcdir)/Makefile.defines.in
diff --git a/lib/silcclient/tests/test_silcclient.c b/lib/silcclient/tests/test_silcclient.c
new file mode 100644 (file)
index 0000000..68d5f90
--- /dev/null
@@ -0,0 +1,456 @@
+/* SILC Client library tests */
+
+#include "silc.h"
+#include "silcclient.h"
+
+SilcBool success;
+SilcClientOperations ops;
+
+/* XXX */
+SilcChannelUser silc_client_on_channel(SilcChannelEntry channel,
+                                       SilcClientEntry client_entry)
+{
+  return NULL;
+}
+
+SilcBuffer silc_client_attributes_request(SilcAttribute attribute, ...)
+{
+  return NULL;
+}
+
+SilcBool silc_client_del_channel_private_keys(SilcClient client,
+                                              SilcClientConnection conn,
+                                              SilcChannelEntry channel)
+{
+  return FALSE;
+}
+
+
+/******* MyBot code **********************************************************/
+
+/* This is context for our MyBot client */
+typedef struct {
+  SilcClient client;           /* The actual SILC Client */
+  SilcClientConnection conn;   /* Connection to the server */
+  SilcPublicKey public_key;     /* My public key */
+  SilcPrivateKey private_key;   /* My private key */
+} *MyBot;
+
+/* Connect callback */
+
+static void
+silc_connected(SilcClient client, SilcClientConnection conn,
+              SilcClientConnectionStatus status, void *context)
+{
+  MyBot mybot = client->application;
+
+  if (status == SILC_CLIENT_CONN_DISCONNECTED) {
+    SILC_LOG_DEBUG(("Disconnected"));
+    silc_client_stop(client);
+    return;
+  }
+
+  if (status != SILC_CLIENT_CONN_SUCCESS &&
+      status != SILC_CLIENT_CONN_SUCCESS_RESUME) {
+    SILC_LOG_DEBUG(("Error connecting to server %d", status));
+    silc_client_stop(client);
+    return;
+  }
+
+  SILC_LOG_DEBUG(("Connected to server"));
+
+  /* Save the connection context */
+  mybot->conn = conn;
+}
+
+/* Start the MyBot, by creating the SILC Client entity by using the
+   SILC Client Library API. */
+int mybot_start(void)
+{
+  MyBot mybot;
+  SilcClientParams params;
+
+  /* Allocate the MyBot structure */
+  mybot = silc_calloc(1, sizeof(*mybot));
+  if (!mybot) {
+    perror("Out of memory");
+    return 1;
+  }
+
+  memset(&params, 0, sizeof(params));
+  params.threads = TRUE;
+  mybot->client = silc_client_alloc(&ops, &params, mybot, NULL);
+  if (!mybot->client) {
+    perror("Could not allocate SILC Client");
+    return 1;
+  }
+
+  /* Now fill the allocated client with mandatory parameters the library
+     requires: username, hostname and "real name". */
+  mybot->client->username = silc_get_username();
+  mybot->client->hostname = silc_net_localhost();
+  mybot->client->realname = strdup("I am the MyBot");
+
+  /* Now we initialize the client. */
+  if (!silc_client_init(mybot->client)) {
+    perror("Could not init client");
+    return 1;
+  }
+
+  if (!silc_load_key_pair("mybot.pub", "mybot.prv", "",
+                         &mybot->public_key,
+                         &mybot->private_key)) {
+    /* The keys don't exist.  Let's generate us a key pair then!  There's
+       nice ready routine for that too.  Let's do 2048 bit RSA key pair. */
+    fprintf(stdout, "MyBot: Key pair does not exist, generating it.\n");
+    if (!silc_create_key_pair("rsa", 2048, "mybot.pub", "mybot.prv", NULL, "",
+                             &mybot->public_key,
+                             &mybot->private_key, FALSE)) {
+      perror("Could not generated key pair");
+      return 1;
+    }
+  }
+
+  /* And, then we are ready to go.  Since we are really simple client we
+     don't have user interface and we don't have to deal with message loops
+     or interactivity.  That's why we can just hand over the execution
+     to the library by calling silc_client_run.  */
+  silc_client_run(mybot->client);
+
+  /* When we get here, we have quit the client, so clean up and exit */
+  silc_client_free(mybot->client);
+  silc_free(mybot);
+  return 0;
+}
+
+/******* SILC Client Operations **********************************************/
+
+/* The SILC Client Library requires these "client operations".  They are
+   functions that the library may call at any time to indicate to application
+   that something happened, like message was received, or authentication
+   is required or something else.  Since our MyBot is really simple client
+   we don't need most of the operations, so we just define them and don't
+   do anything in them. */
+
+static void
+silc_running(SilcClient client, void *application)
+{
+  MyBot mybot = application;
+
+  SILC_LOG_DEBUG(("Client is running"));
+
+  /* Start connecting to server.  This is asynchronous connecting so the
+     connection is actually created later after we run the client. */
+  silc_client_connect_to_server(mybot->client, NULL,
+                               mybot->public_key, mybot->private_key,
+                               "10.2.1.100", 1334,
+                               silc_connected, mybot);
+}
+
+
+/* "say" client operation is a message from the client library to the
+   application.  It may include error messages or something else.  We
+   just dump them to screen. */
+
+static void
+silc_say(SilcClient client, SilcClientConnection conn,
+        SilcClientMessageType type, char *msg, ...)
+{
+  char str[200];
+  va_list va;
+  va_start(va, msg);
+  vsnprintf(str, sizeof(str) - 1, msg, va);
+  fprintf(stdout, "MyBot: %s\n", str);
+  va_end(va);
+}
+
+
+/* Message for a channel. The `sender' is the sender of the message
+   The `channel' is the channel. The `message' is the message.  Note
+   that `message' maybe NULL.  The `flags' indicates message flags
+   and it is used to determine how the message can be interpreted
+   (like it may tell the message is multimedia message). */
+
+static void
+silc_channel_message(SilcClient client, SilcClientConnection conn,
+                    SilcClientEntry sender, SilcChannelEntry channel,
+                    SilcMessagePayload payload,
+                    SilcChannelPrivateKey key,
+                    SilcMessageFlags flags, const unsigned char *message,
+                    SilcUInt32 message_len)
+{
+  /* Yay! We got a message from channel. */
+
+  if (flags & SILC_MESSAGE_FLAG_SIGNED)
+    fprintf(stdout, "[SIGNED] <%s> %s\n", sender->nickname, message);
+  else
+    fprintf(stdout, "<%s> %s\n", sender->nickname, message);
+}
+
+
+/* Private message to the client. The `sender' is the sender of the
+   message. The message is `message'and maybe NULL.  The `flags'  
+   indicates message flags  and it is used to determine how the message
+   can be interpreted (like it may tell the message is multimedia
+   message). */
+
+static void
+silc_private_message(SilcClient client, SilcClientConnection conn,
+                    SilcClientEntry sender, SilcMessagePayload payload,
+                    SilcMessageFlags flags,
+                    const unsigned char *message,
+                    SilcUInt32 message_len)
+{
+  /* MyBot does not support private message receiving */
+}
+
+
+/* Notify message to the client. The notify arguments are sent in the
+   same order as servers sends them. The arguments are same as received
+   from the server except for ID's.  If ID is received application receives
+   the corresponding entry to the ID. For example, if Client ID is received
+   application receives SilcClientEntry.  Also, if the notify type is
+   for channel the channel entry is sent to application (even if server
+   does not send it because client library gets the channel entry from
+   the Channel ID in the packet's header). */
+
+static void
+silc_notify(SilcClient client, SilcClientConnection conn,
+           SilcNotifyType type, ...)
+{
+  char *str;
+  va_list va;
+
+  va_start(va, type);
+
+  /* Here we can receive all kinds of different data from the server, but
+     our simple bot is interested only in receiving the "not-so-important"
+     stuff, just for fun. :) */
+  switch (type) {
+  case SILC_NOTIFY_TYPE_NONE:
+    /* Received something that we are just going to dump to screen. */
+    str = va_arg(va, char *);
+    fprintf(stdout, "--- %s\n", str);
+    break;
+
+  case SILC_NOTIFY_TYPE_MOTD:
+    /* Received the Message of the Day from the server. */
+    str = va_arg(va, char *);
+    fprintf(stdout, "%s", str);
+    fprintf(stdout, "\n");
+    break;
+
+  default:
+    /* Ignore rest */
+    break;
+  }
+
+  va_end(va);
+}
+
+
+/* Command handler. This function is called always in the command function.
+   If error occurs it will be called as well. `conn' is the associated
+   client connection. `cmd_context' is the command context that was
+   originally sent to the command. `success' is FALSE if error occurred
+   during command. `command' is the command being processed. It must be
+   noted that this is not reply from server. This is merely called just
+   after application has called the command. Just to tell application
+   that the command really was processed. */
+
+static void
+silc_command(SilcClient client, SilcClientConnection conn,
+            SilcBool success, SilcCommand command, SilcStatus status,
+            SilcUInt32 argc, unsigned char **argv)
+{
+  /* If error occurred in client library with our command, print the error */
+  if (status != SILC_STATUS_OK)
+    fprintf(stderr, "MyBot: COMMAND %s: %s\n",
+           silc_get_command_name(command),
+           silc_get_status_message(status));
+}
+
+
+/* Command reply handler. This function is called always in the command reply
+   function. If error occurs it will be called as well. Normal scenario
+   is that it will be called after the received command data has been parsed
+   and processed. The function is used to pass the received command data to
+   the application.
+
+   `conn' is the associated client connection. `cmd_payload' is the command
+   payload data received from server and it can be ignored. It is provided
+   if the application would like to re-parse the received command data,
+   however, it must be noted that the data is parsed already by the library
+   thus the payload can be ignored. `success' is FALSE if error occurred.
+   In this case arguments are not sent to the application. The `status' is
+   the command reply status server returned. The `command' is the command
+   reply being processed. The function has variable argument list and each
+   command defines the number and type of arguments it passes to the
+   application (on error they are not sent). */
+
+static void
+silc_command_reply(SilcClient client, SilcClientConnection conn,
+                  SilcCommand command, SilcStatus status,
+                  SilcStatus error, va_list ap)
+{
+  /* If error occurred in client library with our command, print the error */
+  if (status != SILC_STATUS_OK)
+    fprintf(stderr, "MyBot: COMMAND REPLY %s: %s\n",
+           silc_get_command_name(command),
+           silc_get_status_message(status));
+
+}
+
+/* Find authentication method and authentication data by hostname and
+   port. The hostname may be IP address as well. When the authentication
+   method has been resolved the `completion' callback with the found
+   authentication method and authentication data is called. The `conn'
+   may be NULL. */
+
+static void
+silc_get_auth_method(SilcClient client, SilcClientConnection conn,
+                    char *hostname, SilcUInt16 port,
+                    SilcGetAuthMeth completion,
+                    void *context)
+{
+  /* MyBot assumes that there is no authentication requirement in the
+     server and sends nothing as authentication.  We just reply with
+     TRUE, meaning we know what is the authentication method. :). */
+  completion(TRUE, SILC_AUTH_NONE, NULL, 0, context);
+}
+
+
+/* Verifies received public key. The `conn_type' indicates which entity
+   (server, client etc.) has sent the public key. If user decides to trust
+   the application may save the key as trusted public key for later
+   use. The `completion' must be called after the public key has been
+   verified. */
+
+static void
+silc_verify_public_key(SilcClient client, SilcClientConnection conn,
+                      SilcConnectionType conn_type,
+                      SilcPublicKey public_key,
+                      SilcVerifyPublicKey completion, void *context)
+{
+  silc_show_public_key(public_key);
+  completion(TRUE, context);
+}
+
+
+/* Ask (interact, that is) a passphrase from user. The passphrase is
+   returned to the library by calling the `completion' callback with
+   the `context'. The returned passphrase SHOULD be in UTF-8 encoded,
+   if not then the library will attempt to encode. */
+
+static void
+silc_ask_passphrase(SilcClient client, SilcClientConnection conn,
+                   SilcAskPassphrase completion, void *context)
+{
+  /* MyBot does not support asking passphrases from users since there
+     is no user in our little client.  We just reply with nothing. */
+  completion(NULL, 0, context);
+}
+
+
+/* Asks whether the user would like to perform the key agreement protocol.
+   This is called after we have received an key agreement packet or an
+   reply to our key agreement packet. This returns TRUE if the user wants
+   the library to perform the key agreement protocol and FALSE if it is not
+   desired (application may start it later by calling the function
+   silc_client_perform_key_agreement). If TRUE is returned also the
+   `completion' and `context' arguments must be set by the application. */
+
+static bool
+silc_key_agreement(SilcClient client, SilcClientConnection conn,
+                  SilcClientEntry client_entry, const char *hostname,
+                  SilcUInt16 port, SilcKeyAgreementCallback *completion,
+                  void **context)
+{
+  /* MyBot does not support incoming key agreement protocols, it's too
+     simple for that. */
+  return FALSE;
+}
+
+
+/* Notifies application that file transfer protocol session is being
+   requested by the remote client indicated by the `client_entry' from
+   the `hostname' and `port'. The `session_id' is the file transfer
+   session and it can be used to either accept or reject the file
+   transfer request, by calling the silc_client_file_receive or
+   silc_client_file_close, respectively. */
+
+static void
+silc_ftp(SilcClient client, SilcClientConnection conn,
+        SilcClientEntry client_entry, SilcUInt32 session_id,
+        const char *hostname, SilcUInt16 port)
+{
+  /* MyBot does not support file transfer, it's too simple for that too. */
+}
+
+
+/* Delivers SILC session detachment data indicated by `detach_data' to the
+   application.  If application has issued SILC_COMMAND_DETACH command
+   the client session in the SILC network is not quit.  The client remains
+   in the network but is detached.  The detachment data may be used later
+   to resume the session in the SILC Network.  The appliation is
+   responsible of saving the `detach_data', to for example in a file.
+
+   The detachment data can be given as argument to the functions
+   silc_client_connect_to_server, or silc_client_add_connection when
+   creating connection to remote server, inside SilcClientConnectionParams
+   structure.  If it is provided the client library will attempt to resume
+   the session in the network.  After the connection is created
+   successfully, the application is responsible of setting the user
+   interface for user into the same state it was before detaching (showing
+   same channels, channel modes, etc).  It can do this by fetching the
+   information (like joined channels) from the client library. */
+
+static void
+silc_detach(SilcClient client, SilcClientConnection conn,
+           const unsigned char *detach_data, SilcUInt32 detach_data_len)
+{
+  /* Oh, and MyBot does not support session detaching either. */
+}
+
+/* Our client operations for the MyBot.  This structure is filled with
+   functions and given as argument to the silc_client_alloc function.
+   Even though our little bot does not need all these functions we must
+   provide them since the SILC Client Library wants them all. */
+/* This structure and all the functions were taken from the
+   lib/silcclient/client_ops_example.c. */
+SilcClientOperations ops = {
+  silc_say,
+  silc_channel_message,
+  silc_private_message,
+  silc_notify,
+  silc_command,
+  silc_command_reply,
+  silc_get_auth_method,
+  silc_verify_public_key,
+  silc_ask_passphrase,
+  silc_key_agreement,
+  silc_ftp,
+  silc_detach,
+  silc_running
+};
+
+int main(int argc, char **argv)
+{
+  SilcSchedule schedule;
+
+  if (argc > 1 && !strcmp(argv[1], "-d")) {
+    silc_log_debug(TRUE);
+    silc_log_debug_hexdump(TRUE);
+    silc_log_quick(TRUE);
+    silc_log_set_debug_string("*client*,*packet*,*net*,*stream*,*ske*,*buffer*");
+  }
+
+  /* Start the bot */
+  mybot_start();
+
+ err:
+  SILC_LOG_DEBUG(("Testing was %s", success ? "SUCCESS" : "FAILURE"));
+  fprintf(stderr, "Testing was %s\n", success ? "SUCCESS" : "FAILURE");
+
+  return success;
+}