Merge commit 'origin/silc.1.1.branch'
[silc.git] / lib / silcclient / client.c
index 8834c272f92278f5a263594520cc3e9ac9e6d89a..449fc375ce152b33bca4e0c3bbd06ce53870766d 100644 (file)
@@ -4,7 +4,7 @@
 
   Author: Pekka Riikonen <priikone@silcnet.org>
 
-  Copyright (C) 1997 - 2006 Pekka Riikonen
+  Copyright (C) 1997 - 2008 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
   GNU General Public License for more details.
 
 */
-/* $Id$ */
 
 #include "silc.h"
 #include "silcclient.h"
 #include "client_internal.h"
 
-/************************** Types and definitions ***************************/
+/************************ Static utility functions **************************/
 
-SILC_FSM_STATE(silc_client_connection_st_run);
-SILC_FSM_STATE(silc_client_new_id);
+/* Connection machine FSM destructor.  This will finish the thread where
+   the machine was running and deletes the connection context. */
 
+static void silc_client_connection_destructor(SilcFSM fsm,
+                                             void *fsm_context,
+                                             void *destructor_context)
+{
+  SilcClientConnection conn = fsm_context;
+  SilcFSMThread thread = destructor_context;
 
-/************************ Static utility functions **************************/
+  SILC_LOG_DEBUG(("Connection %p finished", conn));
+
+  /* Delete connection */
+  silc_client_del_connection(conn->client, conn);
+
+  /* Finish the thread were this machine was running.  Its destructor is the
+     silc_client_connection_finished. */
+  silc_fsm_finish(thread);
+}
+
+/* Connection thread FSM destructor.  This was the thread where the connection
+   machine was running (may be real thread).  From here we notify client
+   that the connection thread has finished. */
+
+static void silc_client_connection_finished(SilcFSMThread fsm,
+                                           void *fsm_context,
+                                           void *destructor_context)
+{
+  SilcClient client = silc_fsm_get_state_context(fsm);
+
+  /* Signal client that we have finished */
+  silc_atomic_sub_int32(&client->internal->conns, 1);
+  client->internal->connection_closed = TRUE;
+  SILC_FSM_EVENT_SIGNAL(&client->internal->wait_event);
+
+  silc_fsm_free(fsm);
+}
+
+/* Packet FSM thread destructor */
+
+static void silc_client_packet_destructor(SilcFSMThread thread,
+                                         void *thread_context,
+                                         void *destructor_context)
+{
+  SilcClientConnection conn = thread_context;
+
+  /* Add thread back to thread pool */
+  silc_list_add(conn->internal->thread_pool, thread);
+  if (silc_list_count(conn->internal->thread_pool) == 1)
+    silc_list_start(conn->internal->thread_pool);
+}
 
 /* Packet engine callback to receive a packet */
 
@@ -38,8 +83,8 @@ static SilcBool silc_client_packet_receive(SilcPacketEngine engine,
                                           void *callback_context,
                                           void *stream_context)
 {
-  SilcClient client = callback_context;
   SilcClientConnection conn = stream_context;
+  SilcFSMThread thread;
 
   /* Packets we do not handle */
   switch (packet->type) {
@@ -50,18 +95,28 @@ static SilcBool silc_client_packet_receive(SilcPacketEngine engine,
   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;
   }
 
-  /* 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);
+  /* Get packet processing thread */
+  thread = silc_list_get(conn->internal->thread_pool);
+  if (!thread) {
+    thread = silc_fsm_thread_alloc(&conn->internal->fsm, conn,
+                                  silc_client_packet_destructor, NULL, FALSE);
+    if (!thread)
+      return FALSE;
+  } else {
+    silc_list_del(conn->internal->thread_pool, thread);
+    silc_fsm_thread_init(thread, &conn->internal->fsm, conn,
+                        silc_client_packet_destructor, NULL, FALSE);
+  }
+
+  /* Process packet in thread */
+  silc_fsm_set_state_context(thread, packet);
+  silc_fsm_start_sync(thread, silc_client_connection_st_packet);
 
   return TRUE;
 }
@@ -73,7 +128,16 @@ static void silc_client_packet_eos(SilcPacketEngine engine,
                                   void *callback_context,
                                   void *stream_context)
 {
-  SILC_LOG_DEBUG(("End of stream received"));
+  SilcClientConnection conn = stream_context;
+
+  SILC_LOG_DEBUG(("Remote disconnected connection"));
+
+  /* Signal to close connection */
+  conn->internal->status = SILC_CLIENT_CONN_DISCONNECTED;
+  if (!conn->internal->disconnected) {
+    conn->internal->disconnected = TRUE;
+    SILC_FSM_EVENT_SIGNAL(&conn->internal->wait_event);
+  }
 }
 
 /* Packet engine callback to indicate error */
@@ -84,7 +148,15 @@ static void silc_client_packet_error(SilcPacketEngine engine,
                                     void *callback_context,
                                     void *stream_context)
 {
+  SilcClient client = callback_context;
+  SilcClientConnection conn = stream_context;
+
+  /* Read and write errors are silent */
+  if (error == SILC_PACKET_ERR_READ || error == SILC_PACKET_ERR_WRITE)
+    return;
 
+  client->internal->ops->say(client, conn, SILC_CLIENT_MESSAGE_ERROR,
+                            (char *)silc_packet_error_string(error));
 }
 
 /* Packet stream callbacks */
@@ -103,6 +175,28 @@ void silc_client_fsm_destructor(SilcFSM fsm, void *fsm_context,
   silc_fsm_free(fsm);
 }
 
+/* Connect abort operation */
+
+static void silc_client_connect_abort(SilcAsyncOperation op, void *context)
+{
+  SilcClientConnection conn = context;
+
+  SILC_LOG_DEBUG(("Connection %p aborted by application", conn));
+
+  /* Connection callback will not be called after user aborted connecting */
+  conn->callback = NULL;
+  conn->internal->cop = NULL;
+
+  /* Signal to close connection */
+  if (!conn->internal->disconnected) {
+    conn->internal->disconnected = TRUE;
+
+    /* If user aborts before connection machine is even up yet, then don't
+       send signal yet.  It will process this event when it comes up. */
+    if (silc_fsm_is_started(&conn->internal->fsm))
+      SILC_FSM_EVENT_SIGNAL(&conn->internal->wait_event);
+  }
+}
 
 /************************** Connection's machine ****************************/
 
@@ -112,96 +206,101 @@ void silc_client_fsm_destructor(SilcFSM fsm, void *fsm_context,
 SILC_FSM_STATE(silc_client_connection_st_start)
 {
   SilcClientConnection conn = fsm_context;
+  SilcFSM connfsm;
 
   /* 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 */
+  connfsm = &conn->internal->fsm;
+  silc_fsm_init(connfsm, conn, silc_client_connection_destructor,
+               fsm, conn->internal->schedule);
+  silc_fsm_event_init(&conn->internal->wait_event, connfsm);
+  silc_fsm_start_sync(connfsm, silc_client_connection_st_run);
+
+  /* Schedule any events possibly set in initialization */
+  if (conn->internal->disconnected)
+    SILC_FSM_EVENT_SIGNAL(&conn->internal->wait_event);
   if (conn->internal->connect)
-    SILC_FSM_SEMA_POST(&conn->internal->wait_event);
+    SILC_FSM_EVENT_SIGNAL(&conn->internal->wait_event);
   if (conn->internal->key_exchange)
-    SILC_FSM_SEMA_POST(&conn->internal->wait_event);
+    SILC_FSM_EVENT_SIGNAL(&conn->internal->wait_event);
 
-  /* Wait until this thread is terminated */
+  /* Wait until this thread is terminated from the machine destructor */
   return SILC_FSM_WAIT;
 }
 
-/* Connection machine main state. */
+/* Connection machine main state.  This handles various connection related
+   events, but not packet processing.  It's done in dedicated packet
+   processing FSM thread. */
 
 SILC_FSM_STATE(silc_client_connection_st_run)
 {
   SilcClientConnection conn = fsm_context;
+  SilcFSMThread thread;
 
   /* Wait for events */
-  SILC_FSM_SEMA_WAIT(&conn->internal->wait_event);
+  SILC_FSM_EVENT_WAIT(&conn->internal->wait_event);
 
   /* Process events */
+  thread = &conn->internal->event_thread;
+
+  if (conn->internal->disconnected) {
+    /** Event: disconnected */
+    SILC_LOG_DEBUG(("Event: disconnected"));
+    silc_fsm_next(fsm, silc_client_connection_st_close);
+    return SILC_FSM_YIELD;
+  }
 
   if (conn->internal->connect) {
     SILC_LOG_DEBUG(("Event: connect"));
     conn->internal->connect = FALSE;
+    SILC_ASSERT(silc_fsm_is_started(thread) == 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);
+    /*** Event: connect */
+    silc_fsm_thread_init(thread, &conn->internal->fsm, conn,
+                        NULL, NULL, FALSE);
+    silc_fsm_start_sync(thread, silc_client_st_connect);
     return SILC_FSM_CONTINUE;
   }
 
   if (conn->internal->key_exchange) {
     SILC_LOG_DEBUG(("Event: key exchange"));
     conn->internal->key_exchange = FALSE;
+    SILC_ASSERT(silc_fsm_is_started(thread) == 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);
+    /*** Event: key exchange */
+    silc_fsm_thread_init(thread, &conn->internal->fsm, conn,
+                        NULL, NULL, FALSE);
+    silc_fsm_start_sync(thread, silc_client_st_connect_set_stream);
     return SILC_FSM_CONTINUE;
   }
 
-  if (conn->internal->disconnected) {
-    SILC_LOG_DEBUG(("Event: disconnected"));
-    conn->internal->disconnected = FALSE;
+  if (conn->internal->rekeying) {
+    SILC_LOG_DEBUG(("Event: rekey"));
+    conn->internal->rekeying = FALSE;
+    SILC_ASSERT(silc_fsm_is_started(thread) == FALSE);
 
+    /*** Event: rekey */
+    silc_fsm_thread_init(thread, &conn->internal->fsm, conn,
+                        NULL, NULL, FALSE);
+    silc_fsm_start_sync(thread, silc_client_st_rekey);
     return SILC_FSM_CONTINUE;
   }
 
   /* NOT REACHED */
-#if defined(SILC_DEBUG)
-  assert(FALSE);
-#endif /* SILC_DEBUG */
+  SILC_ASSERT(FALSE);
   return SILC_FSM_CONTINUE;
 }
 
-/* Connection's packet processor main state.  Packet processor thread waits
-   here for a new packet and processes received packets. */
+/* Packet processor thread.  Each incoming packet is processed in FSM
+   thread in this state.  The thread is run in the connection machine. */
 
 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;
-
   SILC_LOG_DEBUG(("Parsing %s packet", silc_get_packet_name(packet->type)));
 
   switch (packet->type) {
@@ -212,18 +311,18 @@ SILC_FSM_STATE(silc_client_connection_st_packet)
     break;
 
   case SILC_PACKET_CHANNEL_MESSAGE:
-    /* Channel message */
-    //    silc_client_channel_message(client, conn, packet);
+    /** Channel message */
+    silc_fsm_next(fsm, silc_client_channel_message);
     break;
 
   case SILC_PACKET_FTP:
     /* File transfer packet */
-    //    silc_client_ftp(client, conn, packet);
+    silc_fsm_next(fsm, silc_client_ftp);
     break;
 
   case SILC_PACKET_CHANNEL_KEY:
-    /* Received channel key */
-    //    silc_client_channel_key(client, conn, packet);
+    /** Channel key */
+    silc_fsm_next(fsm, silc_client_channel_key);
     break;
 
   case SILC_PACKET_COMMAND_REPLY:
@@ -232,28 +331,28 @@ SILC_FSM_STATE(silc_client_connection_st_packet)
     break;
 
   case SILC_PACKET_NOTIFY:
-    /* Notify */
-    //    silc_client_notify(client, conn, packet);
+    /** Notify */
+    silc_fsm_next(fsm, silc_client_notify);
     break;
 
   case SILC_PACKET_PRIVATE_MESSAGE_KEY:
     /* Private message key indicator */
-    //    silc_client_private_message_key(client, conn, packet);
+    silc_fsm_next(fsm, silc_client_private_message_key);
     break;
 
   case SILC_PACKET_DISCONNECT:
-    /* Server disconnects */
-    //    silc_client_disconnect(client, conn, packet);
+    /** Disconnect */
+    silc_fsm_next(fsm, silc_client_disconnect);
     break;
 
   case SILC_PACKET_ERROR:
     /* Error by server */
-    //    silc_client_error(client, conn, packet);
+    silc_fsm_next(fsm, silc_client_error);
     break;
 
   case SILC_PACKET_KEY_AGREEMENT:
-    /* Key agreement */
-    //    silc_client_key_agreement(client, conn, packet);
+    /** Key agreement */
+    silc_fsm_next(fsm, silc_client_key_agreement);
     break;
 
   case SILC_PACKET_COMMAND:
@@ -264,21 +363,151 @@ SILC_FSM_STATE(silc_client_connection_st_packet)
   case SILC_PACKET_NEW_ID:
     /** New ID */
     silc_fsm_next(fsm, silc_client_new_id);
+    break;
 
   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);
+    /** Connection auth resolve reply */
+    silc_fsm_next(fsm, silc_client_connect_auth_request);
+    break;
+
+  case SILC_PACKET_REKEY:
+    /* Signal to start rekey */
+    conn->internal->rekey_responder = TRUE;
+    conn->internal->rekeying = TRUE;
+    SILC_FSM_EVENT_SIGNAL(&conn->internal->wait_event);
+
+    silc_packet_free(packet);
+    return SILC_FSM_FINISH;
     break;
 
   default:
     silc_packet_free(packet);
+    return SILC_FSM_FINISH;
     break;
   }
 
   return SILC_FSM_CONTINUE;
 }
 
+/* Disconnection event to close remote connection.  We close the connection
+   and finish the connection machine in this state.  The connection context
+   is deleted in the machine destructor.  The connection callback is called
+   in this state if it is set. */
+
+SILC_FSM_STATE(silc_client_connection_st_close)
+{
+  SilcClientConnection conn = fsm_context;
+  SilcClientCommandContext cmd;
+
+  /* Finish running command threads.  This will also finish waiting packet
+     thread, as they are always waiting for some command.  If any thread is
+     waiting something else than command, they must be finished explicitly. */
+  if (silc_list_count(conn->internal->pending_commands)) {
+    SILC_LOG_DEBUG(("Finish pending commands"));
+    silc_list_start(conn->internal->pending_commands);
+    while ((cmd = silc_list_get(conn->internal->pending_commands))) {
+      if (silc_fsm_is_started(&cmd->thread)) {
+        cmd->verbose = FALSE;
+        silc_fsm_continue_sync(&cmd->thread);
+      }
+    }
+
+    /* Give threads time to finish */
+    return SILC_FSM_YIELD;
+  }
+
+  /* Abort ongoing event */
+  if (conn->internal->op) {
+    SILC_LOG_DEBUG(("Abort event"));
+    silc_async_abort(conn->internal->op, NULL, NULL);
+    conn->internal->op = NULL;
+  }
+
+  /* If event thread is running, finish it. */
+  if (silc_fsm_is_started(&conn->internal->event_thread)) {
+    SILC_LOG_DEBUG(("Finish event thread"));
+    silc_fsm_continue_sync(&conn->internal->event_thread);
+    return SILC_FSM_YIELD;
+  }
+
+  /* Call the connection callback */
+  if (conn->callback)
+    conn->callback(conn->client, conn, conn->internal->status,
+                  conn->internal->error, conn->internal->disconnect_message,
+                  conn->callback_context);
+  silc_free(conn->internal->disconnect_message);
+
+  SILC_LOG_DEBUG(("Closing remote connection"));
+
+  /* Close connection. */
+  if (conn->stream)
+    silc_packet_stream_destroy(conn->stream);
+
+  SILC_LOG_DEBUG(("Finishing connection machine"));
+  return SILC_FSM_FINISH;
+}
+
+/* Received error packet from server.  Send it to application. */
+
+SILC_FSM_STATE(silc_client_error)
+{
+  SilcClientConnection conn = fsm_context;
+  SilcClient client = conn->client;
+  SilcPacket packet = state_context;
+  char *msg;
+
+  msg = silc_memdup(silc_buffer_data(&packet->buffer),
+                   silc_buffer_len(&packet->buffer));
+  if (msg)
+    client->internal->ops->say(client, conn, SILC_CLIENT_MESSAGE_AUDIT, msg);
+
+  silc_free(msg);
+  silc_packet_free(packet);
+
+  return SILC_FSM_FINISH;
+}
+
+/* Received disconnect packet from server.  We close the connection and
+   send the disconnect message to application. */
+
+SILC_FSM_STATE(silc_client_disconnect)
+{
+  SilcClientConnection conn = fsm_context;
+  SilcPacket packet = state_context;
+  SilcStatus status;
+  char *message = NULL;
+
+  SILC_LOG_DEBUG(("Server disconnected"));
+
+  if (silc_buffer_len(&packet->buffer) < 1) {
+    silc_packet_free(packet);
+    return SILC_FSM_FINISH;
+  }
+
+  status = (SilcStatus)packet->buffer.data[0];
+
+  silc_buffer_pull(&packet->buffer, 1);
+  if (silc_buffer_len(&packet->buffer) > 1 &&
+      silc_utf8_valid(silc_buffer_data(&packet->buffer),
+                     silc_buffer_len(&packet->buffer)))
+    message = silc_memdup(silc_buffer_data(&packet->buffer),
+                         silc_buffer_len(&packet->buffer));
+
+  /* Call connection callback */
+  conn->internal->status = SILC_CLIENT_CONN_DISCONNECTED;
+  conn->internal->error = status;
+  conn->internal->disconnect_message = message;
+
+  /* Signal to close connection */
+  if (!conn->internal->disconnected) {
+    conn->internal->disconnected = TRUE;
+    SILC_FSM_EVENT_SIGNAL(&conn->internal->wait_event);
+  }
+
+  silc_packet_free(packet);
+
+  return SILC_FSM_FINISH;
+}
 
 /*************************** Main client machine ****************************/
 
@@ -289,101 +518,72 @@ SILC_FSM_STATE(silc_client_st_run)
   SilcClient client = fsm_context;
 
   /* Wait for events */
-  SILC_FSM_SEMA_WAIT(&client->internal->wait_event);
+  SILC_FSM_EVENT_WAIT(&client->internal->wait_event);
 
   /* Process events */
 
-  if (client->internal->run_callback && client->internal->ops->running) {
+  if (client->internal->run_callback) {
     /* Call running callbcak back to application */
     client->internal->run_callback = FALSE;
-    client->internal->ops->running(client, client->application);
+    if (client->internal->running) {
+      SILC_LOG_DEBUG(("We are up, call running callback"));
+      client->internal->running(client, client->internal->running_context);
+    }
+    return SILC_FSM_CONTINUE;
+  }
+
+  if (client->internal->connection_closed) {
+    /* A connection finished */
+    SILC_LOG_DEBUG(("Event: connection closed"));
+    client->internal->connection_closed = FALSE;
+    if (silc_atomic_get_int32(&client->internal->conns) == 0 &&
+       client->internal->stop)
+      SILC_FSM_EVENT_SIGNAL(&client->internal->wait_event);
+    return SILC_FSM_CONTINUE;
+  }
+
+  if (client->internal->stop) {
+    /* Stop client libarry.  If we have running connections, wait until
+       they finish first. */
+    if (silc_atomic_get_int32(&client->internal->conns) == 0) {
+      SILC_LOG_DEBUG(("Event: stop"));
+      silc_fsm_next(fsm, silc_client_st_stop);
+    }
     return SILC_FSM_CONTINUE;
   }
 
   /* NOT REACHED */
-#if defined(SILC_DEBUG)
-  assert(FALSE);
-#endif /* SILC_DEBUG */
+  SILC_ASSERT(FALSE);
   return SILC_FSM_CONTINUE;
 }
 
+/* Stop event.  Stops the client library. */
 
-/**************************** Packet Processing *****************************/
+SILC_FSM_STATE(silc_client_st_stop)
+{
+  SilcClient client = fsm_context;
 
-/* Received new ID from server during registering to SILC network */
+  SILC_LOG_DEBUG(("Client stopped"));
 
-SILC_FSM_STATE(silc_client_new_id)
-{
-  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);
-  }
+  /* Stop scheduler */
+  silc_schedule_stop(client->schedule);
+  silc_client_commands_unregister(client);
 
-  /* 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);
+  /* Call stopped callback to application */
+  if (client->internal->running)
+    client->internal->running(client, client->internal->running_context);
 
- out:
-  /** Packet processed */
-  silc_packet_free(packet);
-  silc_fsm_next(fsm, silc_client_connection_st_packet);
-  return SILC_FSM_CONTINUE;
+  return SILC_FSM_FINISH;
 }
 
+/******************************* Private API ********************************/
 
-/******************************* 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
-   be added to the client using this function. User data `context' may
-   be sent as argument. This function is normally used only if the
-   application performed the connecting outside the library. The library
-   however may use this internally. */
+/* Adds new connection.  Creates the connection context and returns it. */
 
 SilcClientConnection
 silc_client_add_connection(SilcClient client,
                           SilcConnectionType conn_type,
+                          SilcBool connect,
                           SilcClientConnectionParams *params,
                           SilcPublicKey public_key,
                           SilcPrivateKey private_key,
@@ -402,11 +602,6 @@ silc_client_add_connection(SilcClient client,
   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;
-  }
 
   conn->client = client;
   conn->public_key = public_key;
@@ -415,158 +610,188 @@ silc_client_add_connection(SilcClient client,
   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);
-  conn->internal->channel_cache = silc_idcache_alloc(0, SILC_ID_CHANNEL, NULL,
-                                                    NULL);
-  conn->internal->server_cache = silc_idcache_alloc(0, SILC_ID_SERVER, NULL,
-                                                   NULL);
-  conn->internal->ftp_sessions = silc_dlist_init();
+  conn->callback_context = context;
+
+  conn->internal = silc_calloc(1, sizeof(*conn->internal));
+  if (!conn->internal) {
+    silc_free(conn);
+    return NULL;
+  }
+  conn->internal->retry_timer = SILC_CLIENT_RETRY_MIN;
+  silc_mutex_alloc(&conn->internal->lock);
+  silc_atomic_init16(&conn->internal->cmd_ident, 0);
+
+  if (!silc_hash_alloc("sha1", &conn->internal->sha1hash)) {
+    silc_free(conn);
+    silc_free(conn->internal);
+    return NULL;
+  }
+
+  /* Set parameters */
+  if (params)
+    conn->internal->params = *params;
+  if (!conn->internal->params.rekey_secs)
+    conn->internal->params.rekey_secs = 3600;
+#ifndef SILC_DIST_INPLACE
+  if (conn->internal->params.rekey_secs < 300)
+    conn->internal->params.rekey_secs = 300;
+#endif /* SILC_DIST_INPLACE */
+
   conn->internal->verbose = TRUE;
   silc_list_init(conn->internal->pending_commands,
                 struct SilcClientCommandContextStruct, next);
-
-  if (params) {
-    if (params->detach_data)
-      conn->internal->params.detach_data =
-       silc_memdup(params->detach_data,
-                   params->detach_data_len);
-    conn->internal->params.detach_data_len = params->detach_data_len;
+  silc_list_init(conn->internal->thread_pool, SilcFSMThreadStruct, next);
+
+  /* Allocate client, channel and serve caches */
+  if (conn_type != SILC_CONN_CLIENT) {
+    conn->internal->client_cache = silc_idcache_alloc(0, SILC_ID_CLIENT,
+                                                     NULL, NULL);
+    conn->internal->channel_cache = silc_idcache_alloc(0, SILC_ID_CHANNEL,
+                                                      NULL, NULL);
+    conn->internal->server_cache = silc_idcache_alloc(0, SILC_ID_SERVER,
+                                                     NULL, NULL);
+    if (!conn->internal->client_cache || !conn->internal->channel_cache ||
+       !conn->internal->server_cache) {
+      silc_client_del_connection(client, conn);
+      return NULL;
+    }
   }
 
-  /* Add the connection to connections list */
-  //  silc_dlist_add(client->internal->conns, conn);
+  if (connect) {
+    /* Initialize our async operation so that application may abort us
+       while we're connecting. */
+    conn->internal->cop = silc_async_alloc(silc_client_connect_abort,
+                                          NULL, conn);
+    if (!conn->internal->cop) {
+      silc_client_del_connection(client, conn);
+      return NULL;
+    }
+  }
 
-  /* Run the connection state machine.  If threads are in use the machine
-     is always run in a real thread. */
+  /* Run the connection state machine.  If threads are in use the connection
+     machine is always run in a real thread. */
   thread = silc_fsm_thread_alloc(&client->internal->fsm, conn,
-                                silc_client_fsm_destructor, NULL,
+                                silc_client_connection_finished, 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);
+  silc_fsm_set_state_context(thread, client);
+  silc_fsm_start(thread, silc_client_connection_st_start);
+
+  SILC_LOG_DEBUG(("New connection %p", conn));
+  silc_atomic_add_int32(&client->internal->conns, 1);
 
   return conn;
 }
 
-/* Removes connection from client. Frees all memory. */
+/* Deletes connection.  This is always called from the connection machine
+   destructor.  Do not call this directly other places. */
 
 void silc_client_del_connection(SilcClient client, SilcClientConnection conn)
 {
-#if 0
-  SilcClientConnection c;
-  SilcIDCacheList list;
+  SilcList list;
   SilcIDCacheEntry entry;
-  SilcClientCommandPending *r;
-  SilcBool ret;
+  SilcFSMThread thread;
 
-  silc_dlist_start(client->internal->conns);
-  while ((c = silc_dlist_get(client->internal->conns)) != SILC_LIST_END) {
-    if (c != conn)
-      continue;
+  SILC_LOG_DEBUG(("Freeing connection %p", conn));
 
-    /* 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);
-    }
-
-    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_schedule_task_del_by_context(conn->internal->schedule, conn);
 
+  /* Free all cache entries */
+  if (conn->internal->server_cache) {
     if (silc_idcache_get_all(conn->internal->server_cache, &list)) {
-      ret = silc_idcache_list_first(list, &entry);
-      while (ret) {
+      silc_list_start(list);
+      while ((entry = silc_list_get(list)))
        silc_client_del_server(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 (conn->internal->channel_cache) {
+    if (silc_idcache_get_all(conn->internal->channel_cache, &list)) {
+      silc_list_start(list);
+      while ((entry = silc_list_get(list))) {
+       silc_client_empty_channel(client, conn, entry->context);
+       silc_client_del_channel(client, conn, entry->context);
+      }
     }
-
-    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 (conn->internal->client_cache) {
+    if (silc_idcache_get_all(conn->internal->client_cache, &list)) {
+      silc_list_start(list);
+      while ((entry = silc_list_get(list)))
+       silc_client_del_client(client, conn, entry->context);
     }
-
-    silc_free(conn->internal);
-    memset(conn, 0, sizeof(*conn));
-    silc_free(conn);
-
-    silc_dlist_del(client->internal->conns, conn);
   }
-#endif /* 0 */
+
+  /* Free 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 thread pool */
+  silc_list_start(conn->internal->thread_pool);
+  while ((thread = silc_list_get(conn->internal->thread_pool)))
+    silc_fsm_free(thread);
+
+  silc_free(conn->remote_host);
+  silc_buffer_free(conn->internal->local_idp);
+  silc_buffer_free(conn->internal->remote_idp);
+  silc_mutex_free(conn->internal->lock);
+  if (conn->internal->hash)
+    silc_hash_free(conn->internal->hash);
+  if (conn->internal->sha1hash)
+    silc_hash_free(conn->internal->sha1hash);
+  silc_atomic_uninit16(&conn->internal->cmd_ident);
+  silc_free(conn->internal->away_message);
+  if (conn->internal->rekey)
+    silc_ske_free_rekey_material(conn->internal->rekey);
+  if (conn->internal->cop)
+    silc_async_free(conn->internal->cop);
+
+  silc_free(conn->internal);
+  memset(conn, 'F', sizeof(*conn));
+  silc_free(conn);
 }
 
-/* Connects to remote server. This is the main routine used to connect
-   to remote SILC server. Returns FALSE on error. */
+/******************************* Client API *********************************/
 
-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)
+/* Connects to remote server.  This is the main routine used to connect
+   to remote SILC server.  Performs key exchange also.  Returns the
+   connection context to the connection callback. */
+
+SilcAsyncOperation
+silc_client_connect_to_server(SilcClient client,
+                             SilcClientConnectionParams *params,
+                             SilcPublicKey public_key,
+                             SilcPrivateKey private_key,
+                             char *remote_host, int port,
+                             SilcClientConnectCallback callback,
+                             void *context)
 {
   SilcClientConnection conn;
 
+  SILC_LOG_DEBUG(("Connecting to server"));
+
   if (!client || !remote_host)
-    return;
+    return NULL;
+
+  if (client->internal->run_callback) {
+    SILC_LOG_ERROR(("Client library is not started yet. SilcClientRunning "
+                   "callback has not been called yet."));
+    return NULL;
+  }
 
   /* Add new connection */
-  conn = silc_client_add_connection(client, SILC_CONN_SERVER, params,
+  conn = silc_client_add_connection(client, SILC_CONN_SERVER, TRUE, params,
                                    public_key, private_key, remote_host,
                                    port, callback, context);
   if (!conn) {
-    callback(client, NULL, SILC_CLIENT_CONN_ERROR, context);
-    return;
+    callback(client, NULL, SILC_CLIENT_CONN_ERROR, 0, NULL, context);
+    return NULL;
   }
 
   client->internal->ops->say(client, conn, SILC_CLIENT_MESSAGE_AUDIT,
@@ -575,901 +800,115 @@ void silc_client_connect_to_server(SilcClient client,
 
   /* Signal connection machine to start connecting */
   conn->internal->connect = TRUE;
+  return conn->internal->cop;
 }
 
-/* Start SILC Key Exchange (SKE) protocol to negotiate shared secret
-   key material between client and server.  This function can be called
-   directly if application is performing its own connecting and does not
-   use the connecting provided by this library. This function is normally
-   used only if the application performed the connecting outside the library.
-   The library however may use this internally. */
-
-void silc_client_start_key_exchange(SilcClient client,
-                                   SilcClientConnection conn,
-                                   SilcStream stream)
-{
-#if 0
-  assert(conn && stream);
-  assert(client->public_key);
-  assert(client->private_key);
-
-  conn->nickname = (client->nickname ? strdup(client->nickname) :
-                   strdup(client->username));
-#endif /* 0 */
-
-  /* Start */
-
-}
-
-#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
-   the executiong of the protocol in this function. */
-
-void silc_client_resolve_auth_method(SilcBool success,
-                                    SilcProtocolAuthMeth auth_meth,
-                                    const unsigned char *auth_data,
-                                    SilcUInt32 auth_data_len, void *context)
+/* Connects to remote client.  Performs key exchange also.  Returns the
+   connection context to the connection callback. */
+
+SilcAsyncOperation
+silc_client_connect_to_client(SilcClient client,
+                             SilcClientConnectionParams *params,
+                             SilcPublicKey public_key,
+                             SilcPrivateKey private_key,
+                             char *remote_host, int port,
+                             SilcClientConnectCallback callback,
+                             void *context)
 {
-  SilcClientConnAuthInternalContext *proto_ctx =
-    (SilcClientConnAuthInternalContext *)context;
-  SilcClient client = (SilcClient)proto_ctx->client;
-
-  if (!success)
-    auth_meth = SILC_AUTH_NONE;
-
-  proto_ctx->auth_meth = auth_meth;
-
-  if (success && auth_data && auth_data_len) {
-
-    /* Passphrase must be UTF-8 encoded, if it isn't encode it */
-    if (auth_meth == SILC_AUTH_PASSWORD &&
-       !silc_utf8_valid(auth_data, auth_data_len)) {
-      int payload_len = 0;
-      unsigned char *autf8 = NULL;
-      payload_len = silc_utf8_encoded_len(auth_data, auth_data_len,
-                                         SILC_STRING_ASCII);
-      autf8 = silc_calloc(payload_len, sizeof(*autf8));
-      auth_data_len = silc_utf8_encode(auth_data, auth_data_len,
-                                      SILC_STRING_ASCII, autf8, payload_len);
-      auth_data = autf8;
-    }
-
-    proto_ctx->auth_data = silc_memdup(auth_data, auth_data_len);
-    proto_ctx->auth_data_len = auth_data_len;
-  }
+  SilcClientConnection conn;
 
-  /* Allocate the authenteication protocol and execute it. */
-  silc_protocol_alloc(SILC_PROTOCOL_CLIENT_CONNECTION_AUTH,
-                     &proto_ctx->sock->protocol, (void *)proto_ctx,
-                     silc_client_connect_to_server_final);
+  SILC_LOG_DEBUG(("Connecting to client"));
 
-  /* Execute the protocol */
-  silc_protocol_execute(proto_ctx->sock->protocol, client->schedule, 0, 0);
-}
-
-/* Finalizes the connection to the remote SILC server. This is called
-   after authentication protocol has been completed. This send our
-   user information to the server to receive our client ID from
-   server. */
+  if (!client || !remote_host)
+    return NULL;
 
-SILC_TASK_CALLBACK(silc_client_connect_to_server_final)
-{
-  SilcProtocol protocol = (SilcProtocol)context;
-  SilcClientConnAuthInternalContext *ctx =
-    (SilcClientConnAuthInternalContext *)protocol->context;
-  SilcClient client = (SilcClient)ctx->client;
-  SilcClientConnection conn = (SilcClientConnection)ctx->sock->user_data;
-  SilcBuffer packet;
-
-  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 authentication protocol"));
-    ctx->status = SILC_CLIENT_CONN_ERROR_AUTH;
-    goto err;
+  if (client->internal->run_callback) {
+    SILC_LOG_ERROR(("Client library is not started yet. SilcClientRunning "
+                   "callback has not been called yet."));
+    return NULL;
   }
 
-  if (conn->internal->params.detach_data) {
-    /* Send RESUME_CLIENT packet to the server, which is used to resume
-       old detached session back. */
-    SilcBuffer auth;
-    SilcClientID *old_client_id;
-    unsigned char *old_id;
-    SilcUInt16 old_id_len;
-
-    if (!silc_client_process_detach_data(client, conn, &old_id, &old_id_len)) {
-      ctx->status = SILC_CLIENT_CONN_ERROR_RESUME;
-      goto err;
-    }
-
-    old_client_id = silc_id_str2id(old_id, old_id_len, SILC_ID_CLIENT);
-    if (!old_client_id) {
-      silc_free(old_id);
-      ctx->status = SILC_CLIENT_CONN_ERROR_RESUME;
-      goto err;
-    }
-
-    /* Generate authentication data that server will verify */
-    auth = silc_auth_public_key_auth_generate(client->public_key,
-                                             client->private_key,
-                                             client->rng,
-                                             conn->internal->hash,
-                                             old_client_id, SILC_ID_CLIENT);
-    if (!auth) {
-      silc_free(old_client_id);
-      silc_free(old_id);
-      ctx->status = SILC_CLIENT_CONN_ERROR_RESUME;
-      goto err;
-    }
+  if (params)
+    params->no_authentication = TRUE;
 
-    packet = silc_buffer_alloc_size(2 + old_id_len + auth->len);
-    silc_buffer_format(packet,
-                      SILC_STR_UI_SHORT(old_id_len),
-                      SILC_STR_UI_XNSTRING(old_id, old_id_len),
-                      SILC_STR_UI_XNSTRING(auth->data, auth->len),
-                      SILC_STR_END);
-
-    /* Send the packet */
-    silc_client_packet_send(client, ctx->sock, SILC_PACKET_RESUME_CLIENT,
-                           NULL, 0, NULL, NULL,
-                           packet->data, packet->len, TRUE);
-    silc_buffer_free(packet);
-    silc_buffer_free(auth);
-    silc_free(old_client_id);
-    silc_free(old_id);
-  } else {
-    /* Send NEW_CLIENT packet to the server. We will become registered
-       to the SILC network after sending this packet and we will receive
-       client ID from the server. */
-    packet = silc_buffer_alloc(2 + 2 + strlen(client->username) +
-                              strlen(client->realname));
-    silc_buffer_pull_tail(packet, SILC_BUFFER_END(packet));
-    silc_buffer_format(packet,
-                      SILC_STR_UI_SHORT(strlen(client->username)),
-                      SILC_STR_UI_XNSTRING(client->username,
-                                           strlen(client->username)),
-                      SILC_STR_UI_SHORT(strlen(client->realname)),
-                      SILC_STR_UI_XNSTRING(client->realname,
-                                           strlen(client->realname)),
-                      SILC_STR_END);
-
-    /* Send the packet */
-    silc_client_packet_send(client, ctx->sock, SILC_PACKET_NEW_CLIENT,
-                           NULL, 0, NULL, NULL,
-                           packet->data, packet->len, TRUE);
-    silc_buffer_free(packet);
+  /* Add new connection */
+  conn = silc_client_add_connection(client, SILC_CONN_CLIENT, TRUE, params,
+                                   public_key, private_key, remote_host,
+                                   port, callback, context);
+  if (!conn) {
+    callback(client, NULL, SILC_CLIENT_CONN_ERROR, 0, NULL, context);
+    return NULL;
   }
 
-  /* Save remote ID. */
-  conn->remote_id = ctx->dest_id;
-  conn->remote_id_data = silc_id_id2str(ctx->dest_id, SILC_ID_SERVER);
-  conn->remote_id_data_len = silc_id_get_len(ctx->dest_id, SILC_ID_SERVER);
-
-  /* Register re-key timeout */
-  conn->internal->rekey->timeout = client->internal->params->rekey_secs;
-  conn->internal->rekey->context = (void *)client;
-  silc_schedule_task_add(client->schedule, conn->sock->sock,
-                        silc_client_rekey_callback,
-                        (void *)conn->sock, conn->internal->rekey->timeout, 0,
-                        SILC_TASK_TIMEOUT, SILC_TASK_PRI_NORMAL);
-
-  silc_protocol_free(protocol);
-  silc_free(ctx->auth_data);
-  if (ctx->ske)
-    silc_ske_free(ctx->ske);
-  silc_socket_free(ctx->sock);
-  silc_free(ctx);
-  conn->sock->protocol = NULL;
-  return;
-
- err:
-  silc_protocol_free(protocol);
-  silc_free(ctx->auth_data);
-  silc_free(ctx->dest_id);
-  if (ctx->ske)
-    silc_ske_free(ctx->ske);
-  conn->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_auth, ctx,
-                        0, 1, SILC_TASK_TIMEOUT, SILC_TASK_PRI_NORMAL);
+  /* Signal connection machine to start connecting */
+  conn->internal->connect = TRUE;
+  return conn->internal->cop;
 }
 
-/* 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)
+/* Starts key exchange in the remote stream indicated by `stream'.  This
+   creates the connection context and returns it in the connection callback. */
+
+SilcAsyncOperation
+silc_client_key_exchange(SilcClient client,
+                        SilcClientConnectionParams *params,
+                        SilcPublicKey public_key,
+                        SilcPrivateKey private_key,
+                        SilcStream stream,
+                        SilcConnectionType conn_type,
+                        SilcClientConnectCallback callback,
+                        void *context)
 {
-  SilcClient client = (SilcClient)context;
-  SilcClientConnection conn = NULL;
   SilcClientConnection conn;
-  int ret;
-
-  SILC_LOG_DEBUG(("Processing packet"));
-
-  SILC_CLIENT_GET_SOCK(client, fd, sock);
-  if (sock == NULL)
-    return;
-
-  conn = (SilcClientConnection)sock->user_data;
-
-  /* Packet sending */
-  if (type == SILC_TASK_WRITE) {
-    /* Do not send data to disconnected connection */
-    if (SILC_IS_DISCONNECTED(sock))
-      return;
-
-    ret = silc_packet_send(sock, TRUE);
-
-    /* If returned -2 could not write to connection now, will do
-       it later. */
-    if (ret == -2)
-      return;
+  const char *host;
+  SilcUInt16 port;
 
-    /* Error */
-    if (ret == -1)
-      return;
+  SILC_LOG_DEBUG(("Performing key exchange"));
 
-    /* The packet has been sent and now it is time to set the connection
-       back to only for input. When there is again some outgoing data
-       available for this connection it will be set for output as well.
-       This call clears the output setting and sets it only for input. */
-    SILC_CLIENT_SET_CONNECTION_FOR_INPUT(client->schedule, fd);
-    SILC_UNSET_OUTBUF_PENDING(sock);
-
-    silc_buffer_clear(sock->outbuf);
-    return;
-  }
-
-  /* Packet receiving */
-  if (type == SILC_TASK_READ) {
-    /* Read data from network */
-    ret = silc_packet_receive(sock);
-    if (ret < 0)
-      return;
-
-    /* EOF */
-    if (ret == 0) {
-      SILC_LOG_DEBUG(("Read EOF"));
-
-      /* If connection is disconnecting already we will finally
-        close the connection */
-      if (SILC_IS_DISCONNECTING(sock)) {
-       if (sock == conn->sock && sock->type != SILC_SOCKET_TYPE_CLIENT)
-         client->internal->ops->disconnected(client, conn, 0, NULL);
-       silc_client_close_connection_real(client, sock, conn);
-       return;
-      }
-
-      SILC_LOG_DEBUG(("EOF from connection %d", sock->sock));
-      if (sock == conn->sock && sock->type != SILC_SOCKET_TYPE_CLIENT)
-       client->internal->ops->disconnected(client, conn, 0, NULL);
-      silc_client_close_connection_real(client, sock, conn);
-      return;
-    }
-
-    /* Process the packet. This will call the parser that will then
-       decrypt and parse the 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);
-  }
-}
-
-/* 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;
-  SilcClientConnection conn = 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);
+  if (!client || !stream)
+    return NULL;
 
-    return FALSE;
+  if (client->internal->run_callback) {
+    SILC_LOG_ERROR(("Client library is not started yet. SilcClientRunning "
+                   "callback has not been called yet."));
+    return NULL;
   }
 
-  /* Parse the incoming packet type */
-  silc_client_packet_parse_type(client, sock, packet);
-  silc_packet_context_free(packet);
-  silc_free(parser_context);
-  return TRUE;
-}
-#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.
-   If the `sock' is NULL then the conn->sock will be used.  If `sock' is
-   provided it will be checked whether the sock and `conn->sock' are the
-   same (they can be different, ie. a socket can use `conn' as its
-   connection but `conn->sock' might be actually a different connection
-   than the `sock'). */
-
-#if 0
-void silc_client_close_connection_real(SilcClient client,
-                                      SilcClientConnection conn)
-{
-  int del = FALSE;
-
-  SILC_LOG_DEBUG(("Start"));
-
-  if (!sock && !conn)
-    return;
-
-  if (!sock || (sock && conn->sock == sock))
-    del = TRUE;
-  if (!sock)
-    sock = conn->sock;
-
-  if (!sock) {
-    if (del && conn)
-      silc_client_del_connection(client, conn);
-    return;
+  if (!silc_socket_stream_get_info(stream, NULL, &host, NULL, &port)) {
+    SILC_LOG_ERROR(("Socket stream does not have remote host name set"));
+    callback(client, NULL, SILC_CLIENT_CONN_ERROR, 0, NULL, context);
+    return NULL;
   }
 
-  /* We won't listen for this connection anymore */
-  silc_schedule_unset_listen_fd(client->schedule, sock->sock);
-
-  /* Unregister all tasks */
-  silc_schedule_task_del_by_fd(client->schedule, sock->sock);
-
-  /* Close the actual connection */
-  silc_net_close_connection(sock->sock);
-
-  /* Cancel any active protocol */
-  if (sock->protocol) {
-    if (sock->protocol->protocol->type ==
-       SILC_PROTOCOL_CLIENT_KEY_EXCHANGE ||
-       sock->protocol->protocol->type ==
-       SILC_PROTOCOL_CLIENT_CONNECTION_AUTH) {
-      sock->protocol->state = SILC_PROTOCOL_STATE_ERROR;
-      silc_protocol_execute_final(sock->protocol, client->schedule);
-      /* The application will recall this function with these protocols
-        (the ops->connected client operation). */
-      return;
-    } else {
-      sock->protocol->state = SILC_PROTOCOL_STATE_ERROR;
-      silc_protocol_execute_final(sock->protocol, client->schedule);
-      sock->protocol = NULL;
-    }
+  /* Add new connection */
+  conn = silc_client_add_connection(client, conn_type, TRUE, params,
+                                   public_key, private_key,
+                                   (char *)host, port, callback, context);
+  if (!conn) {
+    callback(client, NULL, SILC_CLIENT_CONN_ERROR, 0, NULL, context);
+    return NULL;
   }
+  conn->internal->user_stream = stream;
 
-  /* Free everything */
-  if (del && sock->user_data)
-    silc_client_del_connection(client, conn);
-
-  silc_socket_free(sock);
+  /* Signal connection to start key exchange */
+  conn->internal->key_exchange = TRUE;
+  return conn->internal->cop;
 }
 
-/* Closes the connection to the remote end */
+/* Closes remote connection */
 
 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. */
-
-void silc_client_disconnect(SilcClient client,
-                           SilcClientConnection conn,
-                           SilcBuffer packet)
-{
-  SilcClientConnection conn;
-  SilcStatus status;
-  char *message = NULL;
-
-  SILC_LOG_DEBUG(("Server disconnected us, sock %d", sock->sock));
-
-  if (packet->len < 1)
-    return;
-
-  status = (SilcStatus)packet->data[0];
-
-  if (packet->len > 1 &&
-      silc_utf8_valid(packet->data + 1, packet->len - 1))
-    message = silc_memdup(packet->data + 1, packet->len - 1);
-
-  conn = (SilcClientConnection)sock->user_data;
-  if (sock == conn->sock && sock->type != SILC_SOCKET_TYPE_CLIENT)
-    client->internal->ops->disconnected(client, conn, status, message);
-
-  silc_free(message);
-
-  SILC_SET_DISCONNECTED(sock);
-
-  /* Close connection through scheduler. */
-  silc_schedule_task_add(client->schedule, sock->sock,
-                        silc_client_disconnected_by_server_later,
-                        client, 0, 1, SILC_TASK_TIMEOUT,
-                        SILC_TASK_PRI_NORMAL);
-}
-
-/* Received error message from server. Display it on the screen.
-   We don't take any action what so ever of the error message. */
-
-void silc_client_error_by_server(SilcClient client,
-                                SilcClientConnection conn,
-                                SilcBuffer message)
-{
-  char *msg;
-
-  msg = silc_memdup(message->data, message->len);
-  client->internal->ops->say(client, sock->user_data,
-                            SILC_CLIENT_MESSAGE_AUDIT, msg);
-  silc_free(msg);
-}
-
-/* Auto-nicking callback to send NICK command to server. */
-
-SILC_TASK_CALLBACK(silc_client_send_auto_nick)
-{
-  SilcClientConnection conn = (SilcClientConnection)context;
-  SilcClient client = conn->client;
-  if (client)
-    silc_client_command_send(client, conn, SILC_COMMAND_NICK,
-                            ++conn->cmd_ident, 1, 1,
-                            client->nickname, strlen(client->nickname));
-}
-
-/* Client session resuming callback.  If the session was resumed
-   this callback is called after the resuming is completed.  This
-   will call the `connect' client operation to the application
-   since it has not been called yet. */
+  SILC_LOG_DEBUG(("Closing connection %p", conn));
 
-static void silc_client_resume_session_cb(SilcClient client,
-                                         SilcClientConnection conn,
-                                         SilcBool success,
-                                         void *context)
-{
-  SilcBuffer sidp;
-
-  /* Notify application that connection is created to server */
-  client->internal->ops->connected(client, conn, success ?
-                                  SILC_CLIENT_CONN_SUCCESS_RESUME :
-                                  SILC_CLIENT_CONN_ERROR_RESUME);
-
-  if (success) {
-    /* Issue INFO command to fetch the real server name and server
-       information and other stuff. */
-    silc_client_command_register(client, SILC_COMMAND_INFO, NULL, NULL,
-                                silc_client_command_reply_info_i, 0,
-                                ++conn->cmd_ident);
-    sidp = silc_id_payload_encode(conn->remote_id, SILC_ID_SERVER);
-    silc_client_command_send(client, conn, SILC_COMMAND_INFO,
-                            conn->cmd_ident, 1, 2, sidp->data, sidp->len);
-    silc_buffer_free(sidp);
+  /* Signal to close connection */
+  conn->internal->status = SILC_CLIENT_CONN_DISCONNECTED;
+  if (!conn->internal->disconnected) {
+    conn->internal->disconnected = TRUE;
+    SILC_FSM_EVENT_SIGNAL(&conn->internal->wait_event);
   }
 }
 
-/* Processes the received new Client ID from server. Old Client ID is
-   deleted from cache and new one is added. */
-
-void silc_client_receive_new_id(SilcClient client,
-                               SilcClientConnection conn,
-                               SilcIDPayload idp)
-{
-  SilcClientConnection conn = (SilcClientConnection)sock->user_data;
-  int connecting = FALSE;
-  SilcClientID *client_id = silc_id_payload_get_id(idp);
-  char *nickname;
-
-  if (!conn->local_entry)
-    connecting = TRUE;
-
-  /* Delete old ID from ID cache */
-  if (conn->local_id) {
-    /* Check whether they are different */
-    if (SILC_ID_CLIENT_COMPARE(conn->local_id, client_id)) {
-      silc_free(client_id);
-      return;
-    }
-
-    silc_idcache_del_by_context(conn->internal->client_cache,
-                               conn->local_entry);
-    silc_free(conn->local_id);
-  }
-
-  /* Save the new ID */
-
-  if (conn->local_id_data)
-    silc_free(conn->local_id_data);
-
-  conn->local_id = client_id;
-  conn->local_id_data = silc_id_payload_get_data(idp);
-  conn->local_id_data_len = silc_id_payload_get_len(idp);;
-
-  if (!conn->local_entry)
-    conn->local_entry = silc_calloc(1, sizeof(*conn->local_entry));
-
-  conn->local_entry->nickname = conn->nickname;
-  if (!conn->local_entry->username)
-    conn->local_entry->username = strdup(client->username);
-  if (!conn->local_entry->server)
-    conn->local_entry->server = strdup(conn->remote_host);
-  conn->local_entry->id = conn->local_id;
-  conn->local_entry->valid = TRUE;
-  if (!conn->local_entry->channels)
-    conn->local_entry->channels = silc_hash_table_alloc(1, silc_hash_ptr,
-                                                       NULL, NULL,
-                                                       NULL, NULL, NULL,
-                                                       TRUE);
-
-  /* Normalize nickname */
-  nickname = silc_identifier_check(conn->nickname, strlen(conn->nickname),
-                                  SILC_STRING_UTF8, 128, NULL);
-  if (!nickname)
-    return;
-
-    /* Put it to the ID cache */
-  silc_idcache_add(conn->internal->client_cache, nickname, conn->local_id,
-                  (void *)conn->local_entry, 0, NULL);
-
-#if 0
-  if (connecting) {
-    SilcBuffer sidp;
-
-    /* Issue IDENTIFY command for itself to get resolved hostname
-       correctly from server. */
-    silc_client_command_register(client, SILC_COMMAND_IDENTIFY, NULL, NULL,
-                                silc_client_command_reply_identify_i, 0,
-                                ++conn->cmd_ident);
-    sidp = silc_id_payload_encode(conn->local_entry->id, SILC_ID_CLIENT);
-    silc_client_command_send(client, conn, SILC_COMMAND_IDENTIFY,
-                            conn->cmd_ident, 1, 5, sidp->data, sidp->len);
-    silc_buffer_free(sidp);
-
-    if (!conn->internal->params.detach_data) {
-      /* 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_schedule_task_add(client->schedule, 0,
-                              silc_client_send_auto_nick, conn,
-                              1, 0, SILC_TASK_TIMEOUT, SILC_TASK_PRI_NORMAL);
-
-      /* Notify application of successful connection. We do it here now that
-        we've received the Client ID and are allowed to send traffic. */
-      client->internal->ops->connected(client, conn, SILC_CLIENT_CONN_SUCCESS);
-
-      /* Issue INFO command to fetch the real server name and server
-        information and other stuff. */
-      silc_client_command_register(client, SILC_COMMAND_INFO, NULL, NULL,
-                                  silc_client_command_reply_info_i, 0,
-                                  ++conn->cmd_ident);
-      sidp = silc_id_payload_encode(conn->remote_id, SILC_ID_SERVER);
-      silc_client_command_send(client, conn, SILC_COMMAND_INFO,
-                              conn->cmd_ident, 1, 2, sidp->data, sidp->len);
-      silc_buffer_free(sidp);
-    } else {
-      /* We are resuming session.  Start resolving informations from the
-        server we need to set the client libary in the state before
-        detaching the session.  The connect client operation is called
-        after this is successfully completed */
-      silc_client_resume_session(client, conn, silc_client_resume_session_cb,
-                                NULL);
-    }
-  }
-#endif /* 0 */
-}
-
-/* Removes a client entry from all channels it has joined. */
-
-void silc_client_remove_from_channels(SilcClient client,
-                                     SilcClientConnection conn,
-                                     SilcClientEntry client_entry)
-{
-  SilcHashTableList htl;
-  SilcChannelUser chu;
-
-  silc_hash_table_list(client_entry->channels, &htl);
-  while (silc_hash_table_get(&htl, NULL, (void *)&chu)) {
-    silc_hash_table_del(chu->client->channels, chu->channel);
-    silc_hash_table_del(chu->channel->user_list, chu->client);
-    silc_free(chu);
-  }
-
-  silc_hash_table_list_reset(&htl);
-}
-
-/* Replaces `old' client entries from all channels to `new' client entry.
-   This can be called for example when nickname changes and old ID entry
-   is replaced from ID cache with the new one. If the old ID entry is only
-   updated, then this fucntion needs not to be called. */
-
-void silc_client_replace_from_channels(SilcClient client,
-                                      SilcClientConnection conn,
-                                      SilcClientEntry old,
-                                      SilcClientEntry new)
-{
-  SilcHashTableList htl;
-  SilcChannelUser chu;
-
-  silc_hash_table_list(old->channels, &htl);
-  while (silc_hash_table_get(&htl, NULL, (void *)&chu)) {
-    /* Replace client entry */
-    silc_hash_table_del(chu->client->channels, chu->channel);
-    silc_hash_table_del(chu->channel->user_list, chu->client);
-
-    chu->client = new;
-    silc_hash_table_add(chu->channel->user_list, chu->client, chu);
-    silc_hash_table_add(chu->client->channels, chu->channel, chu);
-  }
-  silc_hash_table_list_reset(&htl);
-}
-
-/* Registers failure timeout to process the received failure packet
-   with timeout. */
-
-void silc_client_process_failure(SilcClient client,
-                                SilcClientConnection conn,
-                                SilcPacketContext *packet)
-{
-  SilcUInt32 failure = 0;
-
-  if (sock->protocol) {
-    if (packet->buffer->len >= 4)
-      SILC_GET32_MSB(failure, packet->buffer->data);
-
-    /* Notify application */
-    client->internal->ops->failure(client, sock->user_data, sock->protocol,
-                                  SILC_32_TO_PTR(failure));
-  }
-}
-
-/* A timeout callback for the re-key. We will be the initiator of the
-   re-key protocol. */
-
-SILC_TASK_CALLBACK_GLOBAL(silc_client_rekey_callback)
-{
-  SilcClientConnection conn = (SilcSocketConnection)context;
-  SilcClientConnection conn = (SilcClientConnection)sock->user_data;
-  SilcClient client = (SilcClient)conn->internal->rekey->context;
-  SilcProtocol protocol;
-  SilcClientRekeyInternalContext *proto_ctx;
-
-  SILC_LOG_DEBUG(("Start"));
-
-  /* If rekey protocol is active already wait for it to finish */
-  if (sock->protocol && sock->protocol->protocol &&
-      sock->protocol->protocol->type == SILC_PROTOCOL_CLIENT_REKEY)
-    return;
-
-  /* Allocate internal protocol context. This is sent as context
-     to the protocol. */
-  proto_ctx = silc_calloc(1, sizeof(*proto_ctx));
-  proto_ctx->client = (void *)client;
-  proto_ctx->sock = silc_socket_dup(sock);
-  proto_ctx->responder = FALSE;
-  proto_ctx->pfs = conn->internal->rekey->pfs;
-
-  /* Perform rekey protocol. Will call the final callback after the
-     protocol is over. */
-  silc_protocol_alloc(SILC_PROTOCOL_CLIENT_REKEY,
-                     &protocol, proto_ctx, silc_client_rekey_final);
-  sock->protocol = protocol;
-
-  /* Run the protocol */
-  silc_protocol_execute(protocol, client->schedule, 0, 0);
-}
-
-/* The final callback for the REKEY protocol. This will actually take the
-   new key material into use. */
-
-SILC_TASK_CALLBACK(silc_client_rekey_final)
-{
-  SilcProtocol protocol = (SilcProtocol)context;
-  SilcClientRekeyInternalContext *ctx =
-    (SilcClientRekeyInternalContext *)protocol->context;
-  SilcClient client = (SilcClient)ctx->client;
-  SilcClientConnection conn = ctx->sock;
-  SilcClientConnection conn = (SilcClientConnection)sock->user_data;
-
-  SILC_LOG_DEBUG(("Start"));
-
-  if (protocol->state == SILC_PROTOCOL_STATE_ERROR ||
-      protocol->state == SILC_PROTOCOL_STATE_FAILURE) {
-    /* Error occured during protocol */
-    silc_protocol_cancel(protocol, client->schedule);
-    silc_protocol_free(protocol);
-    sock->protocol = NULL;
-    if (ctx->packet)
-      silc_packet_context_free(ctx->packet);
-    if (ctx->ske)
-      silc_ske_free(ctx->ske);
-    silc_socket_free(ctx->sock);
-    silc_free(ctx);
-    return;
-  }
-
-  /* Purge the outgoing data queue to assure that all rekey packets really
-     go to the network before we quit the protocol. */
-  silc_client_packet_queue_purge(client, sock);
-
-  /* Re-register re-key timeout */
-  if (ctx->responder == FALSE)
-    silc_schedule_task_add(client->schedule, sock->sock,
-                          silc_client_rekey_callback,
-                          sock, conn->internal->rekey->timeout, 0,
-                          SILC_TASK_TIMEOUT, SILC_TASK_PRI_NORMAL);
-
-  /* Cleanup */
-  silc_protocol_free(protocol);
-  sock->protocol = NULL;
-  if (ctx->packet)
-    silc_packet_context_free(ctx->packet);
-  if (ctx->ske)
-    silc_ske_free(ctx->ske);
-  silc_socket_free(ctx->sock);
-  silc_free(ctx);
-}
-
-/* Processes incoming connection authentication method request packet.
-   It is a reply to our previously sent request. The packet can be used
-   to resolve the authentication method for the current session if the
-   client does not know it beforehand. */
-
-void silc_client_connection_auth_request(SilcClient client,
-                                        SilcClientConnection conn,
-                                        SilcPacketContext *packet)
-{
-  SilcClientConnection conn = (SilcClientConnection)sock->user_data;
-  SilcUInt16 conn_type, auth_meth;
-  int ret;
-
-  /* If we haven't send our request then ignore this one. */
-  if (!conn->internal->connauth)
-    return;
-
-  /* Parse the payload */
-  ret = silc_buffer_unformat(packet->buffer,
-                            SILC_STR_UI_SHORT(&conn_type),
-                            SILC_STR_UI_SHORT(&auth_meth),
-                            SILC_STR_END);
-  if (ret == -1)
-    auth_meth = SILC_AUTH_NONE;
-
-  /* Call the request callback to notify application for received
-     authentication method information. */
-  if (conn->internal->connauth->callback)
-    (*conn->internal->connauth->callback)(client, conn, auth_meth,
-                                         conn->internal->connauth->context);
-
-  silc_schedule_task_del(client->schedule, conn->internal->connauth->timeout);
-
-  silc_free(conn->internal->connauth);
-  conn->internal->connauth = NULL;
-}
-
-/* Timeout task callback called if the server does not reply to our
-   connection authentication method request in the specified time interval. */
-
-SILC_TASK_CALLBACK(silc_client_request_authentication_method_timeout)
-{
-  SilcClientConnection conn = (SilcClientConnection)context;
-  SilcClient client = conn->client;
-
-  if (!conn->internal->connauth)
-    return;
-
-  /* Call the request callback to notify application */
-  if (conn->internal->connauth->callback)
-    (*conn->internal->connauth->callback)(client, conn, SILC_AUTH_NONE,
-                                         conn->internal->connauth->context);
-
-  silc_free(conn->internal->connauth);
-  conn->internal->connauth = NULL;
-}
-
-/* This function can be used to request the current authentication method
-   from the server. This may be called when connecting to the server
-   and the client library requests the authentication data from the
-   application. If the application does not know the current authentication
-   method it can request it from the server using this function.
-   The `callback' with `context' will be called after the server has
-   replied back with the current authentication method. */
-
-void
-silc_client_request_authentication_method(SilcClient client,
-                                         SilcClientConnection conn,
-                                         SilcConnectionAuthRequest callback,
-                                         void *context)
-{
-  SilcClientConnAuthRequest connauth;
-  SilcBuffer packet;
-
-  assert(client && conn);
-  connauth = silc_calloc(1, sizeof(*connauth));
-  connauth->callback = callback;
-  connauth->context = context;
-
-  if (conn->internal->connauth)
-    silc_free(conn->internal->connauth);
-
-  conn->internal->connauth = connauth;
-
-  /* Assemble the request packet and send it to the server */
-  packet = silc_buffer_alloc(4);
-  silc_buffer_pull_tail(packet, SILC_BUFFER_END(packet));
-  silc_buffer_format(packet,
-                    SILC_STR_UI_SHORT(SILC_SOCKET_TYPE_CLIENT),
-                    SILC_STR_UI_SHORT(SILC_AUTH_NONE),
-                    SILC_STR_END);
-  silc_client_packet_send(client, conn->sock,
-                         SILC_PACKET_CONNECTION_AUTH_REQUEST,
-                         NULL, 0, NULL, NULL,
-                         packet->data, packet->len, FALSE);
-  silc_buffer_free(packet);
-
-  /* Register a timeout in case server does not reply anything back. */
-  connauth->timeout =
-    silc_schedule_task_add(client->schedule, conn->sock->sock,
-                          silc_client_request_authentication_method_timeout,
-                          conn,
-                          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
@@ -1483,9 +922,15 @@ SilcClient silc_client_alloc(SilcClientOperations *ops,
   SilcClient new_client;
 
   new_client = silc_calloc(1, sizeof(*new_client));
+  if (!new_client)
+    return NULL;
   new_client->application = application;
 
   new_client->internal = silc_calloc(1, sizeof(*new_client->internal));
+  if (!new_client->internal) {
+    silc_free(new_client);
+    return NULL;
+  }
   new_client->internal->ops = ops;
   new_client->internal->params =
     silc_calloc(1, sizeof(*new_client->internal->params));
@@ -1496,19 +941,12 @@ SilcClient silc_client_alloc(SilcClientOperations *ops,
   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;
 
+  silc_atomic_init32(&new_client->internal->conns, 0);
+
   return new_client;
 }
 
@@ -1516,89 +954,105 @@ SilcClient silc_client_alloc(SilcClientOperations *ops,
 
 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);
-  }
+  if (client->schedule)
+    silc_schedule_uninit(client->schedule);
+
+  if (client->rng)
+    silc_rng_free(client->rng);
+
+  if (!client->internal->params->dont_register_crypto_library)
+    silc_crypto_uninit();
+
+  if (client->internal->packet_engine)
+    silc_packet_engine_stop(client->internal->packet_engine);
+  if (client->internal->ftp_sessions)
+    silc_dlist_uninit(client->internal->ftp_sessions);
+  if (client->internal->lock)
+    silc_mutex_free(client->internal->lock);
+  silc_atomic_uninit32(&client->internal->conns);
+  silc_free(client->username);
+  silc_free(client->hostname);
+  silc_free(client->realname);
+  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)
+SilcBool silc_client_init(SilcClient client, const char *username,
+                         const char *hostname, const char *realname,
+                         SilcClientRunning running, void *context)
 {
   SILC_LOG_DEBUG(("Initializing client"));
 
-  assert(client);
-  assert(client->username);
-  assert(client->hostname);
-  assert(client->realname);
+  if (!client)
+    return FALSE;
+
+  if (!username || !hostname) {
+    SILC_LOG_ERROR(("Username and hostname must be given to "
+                   "silc_client_init"));
+    return FALSE;
+  }
+  if (!realname)
+    realname = username;
 
   /* 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),
+  if (!silc_identifier_verify(username, strlen(username),
                              SILC_STRING_UTF8, 128)) {
-    SILC_LOG_ERROR(("Malformed username '%s'", client->username));
+    SILC_LOG_ERROR(("Malformed username '%s'. Username must be UTF-8 string",
+                   client->username));
     return FALSE;
   }
-  if (!silc_identifier_verify(client->hostname, strlen(client->hostname),
+  if (!silc_identifier_verify(hostname, strlen(hostname),
                              SILC_STRING_UTF8, 256)) {
-    SILC_LOG_ERROR(("Malformed hostname '%s'", client->hostname));
+    SILC_LOG_ERROR(("Malformed hostname '%s'. Hostname must be UTF-8 string",
+                   client->hostname));
     return FALSE;
   }
-  if (!silc_utf8_valid(client->realname, strlen(client->realname))) {
-    SILC_LOG_ERROR(("Malformed realname '%s'", client->realname));
+  if (!silc_utf8_valid(realname, strlen(realname))) {
+    SILC_LOG_ERROR(("Malformed realname '%s'. Realname must be UTF-8 string",
+                   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();
-  }
+  /* Take the name strings */
+  client->username = strdup(username);
+  client->hostname = strdup(hostname);
+  client->realname = strdup(realname);
+  if (!username || !hostname || !realname)
+    return FALSE;
+
+  client->internal->ftp_sessions = silc_dlist_init();
+  if (!client->internal->ftp_sessions)
+    return FALSE;
 
-  /* Initialize hash functions for client to use */
-  silc_hash_alloc("md5", &client->md5hash);
-  silc_hash_alloc("sha1", &client->sha1hash);
+  if (!client->internal->params->dont_register_crypto_library)
+    /* Initialize the crypto library.  If application has done this already
+       this has no effect. */
+    silc_crypto_init(NULL);
 
   /* Initialize random number generator */
   client->rng = silc_rng_alloc();
+  if (!client->rng)
+    return FALSE;
   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);
+  client->schedule = silc_schedule_init(0, client, NULL, NULL);
   if (!client->schedule)
     return FALSE;
 
+  /* Allocate client lock */
+  silc_mutex_alloc(&client->internal->lock);
+
+  /* Register commands */
+  silc_client_commands_register(client);
+
   /* Start packet engine */
   client->internal->packet_engine =
     silc_packet_engine_start(client->rng, FALSE, &silc_client_stream_cbs,
@@ -1606,36 +1060,20 @@ SilcBool silc_client_init(SilcClient 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);
+  /* Initialize and start the client FSM */
+  client->internal->running = running;
+  client->internal->running_context = context;
+  silc_fsm_init(&client->internal->fsm, client, NULL, NULL, client->schedule);
+  silc_fsm_event_init(&client->internal->wait_event, &client->internal->fsm);
+  silc_fsm_start_sync(&client->internal->fsm, silc_client_st_run);
 
-  /* Register commands */
-  silc_client_commands_register(client);
+  /* Signal the application when we are running */
+  client->internal->run_callback = TRUE;
+  SILC_FSM_EVENT_SIGNAL(&client->internal->wait_event);
 
   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. */
 
@@ -1643,21 +1081,36 @@ 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. */
+/* Call scheduler one iteration and return. */
 
 void silc_client_run_one(SilcClient client)
 {
-  silc_schedule_one(client->schedule, -1);
+  if (silc_fsm_is_started(&client->internal->fsm))
+    silc_schedule_one(client->schedule, 0);
+}
+
+/* Stops the client. This is called to stop the client and thus to stop
+   the program. */
+
+void silc_client_stop(SilcClient client, SilcClientStopped stopped,
+                     void *context)
+{
+  SILC_LOG_DEBUG(("Stopping client"));
+
+  if (!silc_fsm_is_started(&client->internal->fsm)) {
+    SILC_LOG_WARNING(("Attempting to stop client library before it has been "
+                     "started (silc_client_init not called)"));
+    return;
+  }
+
+  client->internal->running = (SilcClientRunning)stopped;
+  client->internal->running_context = context;
+
+  /* Signal to stop */
+  client->internal->stop = TRUE;
+  SILC_FSM_EVENT_SIGNAL(&client->internal->wait_event);
 }