Merged silc_1_0_branch to trunk.
[silc.git] / lib / silcclient / client.c
index 8403f44a30f8dabef3c9e37b1707f633a008bfdc..9fc554bf5568f91e2d7e4033da3446747e861a33 100644 (file)
@@ -2,15 +2,14 @@
 
   client.c
 
-  Author: Pekka Riikonen <priikone@poseidon.pspt.fi>
+  Author: Pekka Riikonen <priikone@silcnet.org>
 
-  Copyright (C) 1997 - 2000 Pekka Riikonen
+  Copyright (C) 1997 - 2003 Pekka Riikonen
 
   This program is free software; you can redistribute it and/or modify
   it under the terms of the GNU General Public License as published by
-  the Free Software Foundation; either version 2 of the License, or
-  (at your option) any later version.
-  
+  the Free Software Foundation; version 2 of the License.
+
   This program is distributed in the hope that it will be useful,
   but WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 */
 /* $Id$ */
 
-#include "clientlibincludes.h"
+#include "silcincludes.h"
+#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_packet_process);
-SILC_TASK_CALLBACK(silc_client_packet_parse_real);
+SILC_TASK_CALLBACK(silc_client_rekey_final);
 
-static void silc_client_packet_parse(SilcPacketParserContext *parser_context);
-static void silc_client_packet_parse_type(SilcClient client, 
+static bool 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(bool success,
+                                    SilcProtocolAuthMeth auth_meth,
+                                    const unsigned char *auth_data,
+                                    SilcUInt32 auth_data_len, void *context);
 
 /* 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, void *application)
+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->ops = ops;
+
+  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;
 }
 
-/* Free's client object */
+/* Frees client object and its internals. */
 
 void silc_client_free(SilcClient client)
 {
   if (client) {
+    if (client->rng)
+      silc_rng_free(client->rng);
+
+    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);
   }
 }
 
 /* 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. */
+   client. Returns FALSE if error occured, TRUE otherwise. */
 
-int silc_client_init(SilcClient client)
+bool silc_client_init(SilcClient client)
 {
   SILC_LOG_DEBUG(("Initializing client"));
 
+  assert(client);
+  assert(client->username);
+  assert(client->hostname);
+  assert(client->realname);
+
+  /* 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 none cipher */
-  silc_cipher_alloc("none", &client->none_cipher);
+  silc_cipher_alloc("none", &client->internal->none_cipher);
 
   /* Initialize random number generator */
   client->rng = silc_rng_alloc();
   silc_rng_init(client->rng);
-  silc_math_primegen_init(); /* XXX */
+  silc_rng_global_init(client->rng);
 
   /* Register protocols */
   silc_client_protocols_register();
 
   /* Initialize the scheduler */
-  silc_schedule_init(&client->io_queue, &client->timeout_queue, 
-                    &client->generic_queue, 5000);
+  client->schedule =
+    silc_schedule_init(client->internal->params->task_max ?
+                      client->internal->params->task_max : 200, client);
+  if (!client->schedule)
+    return FALSE;
+
+  /* Register commands */
+  silc_client_commands_register(client);
 
   return TRUE;
 }
@@ -95,97 +161,248 @@ void silc_client_stop(SilcClient client)
 {
   SILC_LOG_DEBUG(("Stopping client"));
 
-  /* Stop the scheduler, although it might be already stopped. This
-     doesn't hurt anyone. This removes all the tasks and task queues,
-     as well. */
-  silc_schedule_stop();
-  silc_schedule_uninit();
+  silc_schedule_stop(client->schedule);
+  silc_schedule_uninit(client->schedule);
 
   silc_client_protocols_unregister();
+  silc_client_commands_unregister(client);
 
   SILC_LOG_DEBUG(("Client stopped"));
 }
 
-/* Runs the client. */
+/* 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)
 {
   SILC_LOG_DEBUG(("Running client"));
 
+  assert(client);
+  assert(client->pkcs);
+  assert(client->public_key);
+  assert(client->private_key);
+
   /* Start the scheduler, the heart of the SILC client. When this returns
      the program will be terminated. */
-  silc_schedule();
+  silc_schedule(client->schedule);
+}
+
+/* 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)
+{
+  /* Run the scheduler once. */
+  silc_schedule_one(client->schedule, 0);
+}
+
+static void silc_client_entry_destructor(SilcIDCache cache,
+                                        SilcIDCacheEntry entry)
+{
+  silc_free(entry->name);
 }
 
 /* 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. */
-
-SilcClientConnection silc_client_add_connection(SilcClient client,
-                                               char *hostname,
-                                               int port,
-                                               void *context)
+   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. */
+
+SilcClientConnection
+silc_client_add_connection(SilcClient client,
+                           SilcClientConnectionParams *params,
+                           char *hostname, int port, void *context)
 {
   SilcClientConnection conn;
   int i;
 
+  SILC_LOG_DEBUG(("Adding new connection to %s:%d", hostname, port));
+
   conn = silc_calloc(1, sizeof(*conn));
+  conn->internal = silc_calloc(1, sizeof(*conn->internal));
 
   /* Initialize ID caches */
-  conn->client_cache = silc_idcache_alloc(0);
-  conn->channel_cache = silc_idcache_alloc(0);
-  conn->server_cache = silc_idcache_alloc(0);
   conn->client = client;
   conn->remote_host = strdup(hostname);
   conn->remote_port = port;
   conn->context = context;
-  conn->pending_commands = silc_dlist_init();
+  conn->internal->client_cache =
+    silc_idcache_alloc(0, SILC_ID_CLIENT, silc_client_entry_destructor);
+  conn->internal->channel_cache = silc_idcache_alloc(0, SILC_ID_CHANNEL, NULL);
+  conn->internal->server_cache = silc_idcache_alloc(0, SILC_ID_SERVER, NULL);
+  conn->internal->pending_commands = silc_dlist_init();
+  conn->internal->ftp_sessions = silc_dlist_init();
+
+  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;
+  }
 
   /* Add the connection to connections table */
-  for (i = 0; i < client->conns_count; i++)
-    if (client->conns && !client->conns[i]) {
-      client->conns[i] = conn;
+  for (i = 0; i < client->internal->conns_count; i++)
+    if (client->internal->conns && !client->internal->conns[i]) {
+      client->internal->conns[i] = conn;
       return conn;
     }
 
-  client->conns = silc_realloc(client->conns, sizeof(*client->conns)
-                              * (client->conns_count + 1));
-  client->conns[client->conns_count] = conn;
-  client->conns_count++;
+  client->internal->conns =
+    silc_realloc(client->internal->conns, sizeof(*client->internal->conns)
+                * (client->internal->conns_count + 1));
+  client->internal->conns[client->internal->conns_count] = conn;
+  client->internal->conns_count++;
 
   return conn;
 }
 
-/* Removes connection from client. */
+/* Removes connection from client. Frees all memory. */
 
 void silc_client_del_connection(SilcClient client, SilcClientConnection conn)
 {
   int i;
 
-  for (i = 0; i < client->conns_count; i++)
-    if (client->conns[i] == conn) {
-      if (conn->pending_commands)
-       silc_dlist_uninit(conn->pending_commands);
+  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;
+      bool 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);
+      }
+
+      /* 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;
+      }
+
+      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);
+      }
+
+      silc_free(conn->internal);
+      memset(conn, 0, sizeof(*conn));
       silc_free(conn);
-      client->conns[i] = NULL;
+
+      client->internal->conns[i] = NULL;
     }
 }
 
-/* Internal context for connection process. This is needed as we
-   doing asynchronous connecting. */
-typedef struct {
-  SilcClient client;
-  SilcClientConnection conn;
-  SilcTask task;
-  int sock;
-  char *host;
-  int port;
-  int tries;
-} SilcClientInternalConnectContext;
+/* 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;
+  }
+
+  for (i = 0; i < client->internal->sockets_count; i++) {
+    if (client->internal->sockets[i] == NULL) {
+      client->internal->sockets[i] = silc_socket_dup(sock);
+      return;
+    }
+  }
+
+  client->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. */
+
+void silc_client_del_socket(SilcClient client, SilcSocketConnection sock)
+{
+  int i;
+
+  if (!client->internal->sockets)
+    return;
+
+  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;
+    }
+  }
+}
 
-static int 
+static int
 silc_client_connect_to_server_internal(SilcClientInternalConnectContext *ctx)
 {
   int sock;
@@ -193,19 +410,19 @@ silc_client_connect_to_server_internal(SilcClientInternalConnectContext *ctx)
   /* 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(ctx->port, ctx->host);
+  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_task_register(ctx->client->io_queue, sock, 
-                                silc_client_connect_to_server_start,
-                                (void *)ctx, 0, 0, 
-                                SILC_TASK_FD,
-                                SILC_TASK_PRI_NORMAL);
-  silc_task_reset_iotype(ctx->task, SILC_TASK_WRITE);
-  silc_schedule_set_listen_fd(sock, ctx->task->iomask);
+  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;
 
@@ -213,12 +430,16 @@ silc_client_connect_to_server_internal(SilcClientInternalConnectContext *ctx)
 }
 
 /* Connects to remote server. This is the main routine used to connect
-   to SILC server. Returns -1 on error and the created socket otherwise. 
+   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. */
-
-int silc_client_connect_to_server(SilcClient client, int port,
-                                 char *host, void *context)
+   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)
 {
   SilcClientInternalConnectContext *ctx;
   SilcClientConnection conn;
@@ -227,10 +448,10 @@ int silc_client_connect_to_server(SilcClient client, int port,
   SILC_LOG_DEBUG(("Connecting to port %d of server %s",
                  port, host));
 
-  conn = silc_client_add_connection(client, host, port, context);
+  conn = silc_client_add_connection(client, params, host, port, context);
 
-  client->ops->say(client, conn, 
-                  "Connecting to port %d of server %s", port, host);
+  client->internal->ops->say(client, conn, SILC_CLIENT_MESSAGE_AUDIT,
+                            "Connecting to port %d of server %s", port, host);
 
   /* Allocate internal context for connection process. This is
      needed as we are doing async connecting. */
@@ -238,7 +459,7 @@ int silc_client_connect_to_server(SilcClient client, int port,
   ctx->client = client;
   ctx->conn = conn;
   ctx->host = strdup(host);
-  ctx->port = port;
+  ctx->port = port ? port : 706;
   ctx->tries = 0;
 
   /* Do the actual connecting process */
@@ -248,63 +469,127 @@ int silc_client_connect_to_server(SilcClient client, int port,
   return sock;
 }
 
-/* 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. */
+/* 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. */
 
-int silc_client_start_key_exchange(SilcClient client,
-                                  SilcClientConnection conn,
-                                   int fd)
+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;
-  void *context;
 
-  /* Allocate new socket connection object */
-  silc_socket_alloc(fd, SILC_SOCKET_TYPE_SERVER, (void *)conn, &conn->sock);
-  if (conn->sock == NULL) {
-    client->ops->say(client, conn, 
-                    "Error: Could not allocate connection socket");
-    return FALSE;
-  }
+  SILC_LOG_DEBUG(("Start"));
 
-  conn->nickname = strdup(client->username);
-  conn->sock->hostname = conn->remote_host;
+  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;
 
   /* 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 = conn->sock;
+  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, 
+  silc_protocol_alloc(SILC_PROTOCOL_CLIENT_KEY_EXCHANGE,
                      &protocol, (void *)proto_ctx,
                      silc_client_connect_to_server_second);
   if (!protocol) {
-    client->ops->say(client, conn, 
-                    "Error: Could not start authentication protocol");
-    return FALSE;
+    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);
+    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 
+     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 
+     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(fd);
+  SILC_CLIENT_REGISTER_CONNECTION_FOR_IO(conn->sock->sock);
 
   /* Execute the protocol */
-  protocol->execute(client->timeout_queue, 0, protocol, fd, 0, 0);
-  return TRUE;
+  silc_protocol_execute(protocol, client->schedule, 0, 0);
+}
+
+/* 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,
+                                   int fd)
+{
+  assert(client->pkcs);
+  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));
+
+  /* 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);
+  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,
+                                  SILC_CLIENT_CONN_ERROR);
+  silc_free(ctx);
 }
 
 /* Start of the connection to the remote server. This is called after
@@ -321,57 +606,55 @@ SILC_TASK_CALLBACK(silc_client_connect_to_server_start)
   SILC_LOG_DEBUG(("Start"));
 
   /* Check the socket status as it might be in error */
-  getsockopt(fd, SOL_SOCKET, SO_ERROR, &opt, &opt_len);
+  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->ops->say(client, conn, "Could not connect to server %s: %s",
-                      ctx->host, strerror(opt));
-      client->ops->say(client, conn, 
-                      "Connecting to port %d of server %s resumed", 
-                      ctx->port, ctx->host);
+      client->internal->ops->say(client, conn, SILC_CLIENT_MESSAGE_ERROR,
+                                "Could not connect to server %s: %s",
+                                ctx->host, strerror(opt));
+      client->internal->ops->say(client, conn, SILC_CLIENT_MESSAGE_AUDIT,
+                                "Connecting to port %d of server %s resumed",
+                                ctx->port, ctx->host);
 
       /* Unregister old connection try */
-      silc_schedule_unset_listen_fd(fd);
+      silc_schedule_unset_listen_fd(client->schedule, fd);
       silc_net_close_connection(fd);
-      silc_task_unregister(client->io_queue, ctx->task);
+      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->ops->say(client, conn, "Could not connect to server %s: %s",
-                      ctx->host, strerror(opt));
-      silc_schedule_unset_listen_fd(fd);
+      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_task_unregister(client->io_queue, ctx->task);
+      silc_schedule_task_del(client->schedule, ctx->task);
       silc_free(ctx);
 
       /* Notify application of failure */
-      client->ops->connect(client, conn, FALSE);
-      silc_client_del_connection(client, conn);
+      client->internal->ops->connected(client, conn, SILC_CLIENT_CONN_ERROR);
     }
     return;
   }
 
-  silc_schedule_unset_listen_fd(fd);
-  silc_task_unregister(client->io_queue, ctx->task);
+  silc_schedule_unset_listen_fd(client->schedule, fd);
+  silc_schedule_task_del(client->schedule, ctx->task);
   silc_free(ctx);
 
-  if (!silc_client_start_key_exchange(client, conn, fd)) {
-    silc_net_close_connection(fd);
-    client->ops->connect(client, conn, FALSE);
-  }
+  silc_client_start_key_exchange(client, conn, fd);
 }
 
-/* Second part of the connecting to the server. This executed 
+/* 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 *ctx =
     (SilcClientKEInternalContext *)protocol->context;
   SilcClient client = (SilcClient)ctx->client;
   SilcSocketConnection sock = NULL;
@@ -379,22 +662,38 @@ SILC_TASK_CALLBACK(silc_client_connect_to_server_second)
 
   SILC_LOG_DEBUG(("Start"));
 
-  if (protocol->state == SILC_PROTOCOL_STATE_ERROR) {
+  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 */
-    client->ops->connect(client, ctx->sock->user_data, FALSE);
-    silc_free(ctx);
+    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));
@@ -404,34 +703,69 @@ SILC_TASK_CALLBACK(silc_client_connect_to_server_second)
   proto_ctx->dest_id_type = ctx->dest_id_type;
   proto_ctx->dest_id = ctx->dest_id;
 
-  /* Resolve the authentication method to be used in this connection */
-  if (!client->ops->get_auth_method(client, sock->user_data, sock->hostname,
-                                   sock->port, &proto_ctx->auth_meth,
-                                   &proto_ctx->auth_data, 
-                                   &proto_ctx->auth_data_len))
-    {
-      /* XXX do AUTH_REQUEST resolcing with server */
-      proto_ctx->auth_meth = SILC_PROTOCOL_CONN_AUTH_NONE;
-    }
-
   /* 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);
-  /* silc_free(ctx->keymat....); */
   sock->protocol = NULL;
 
-  /* Allocate the authentication protocol. This is allocated here
-     but we won't start it yet. We will be receiving party of this
-     protocol thus we will wait that connecting party will make
-     their first move. */
-  silc_protocol_alloc(SILC_PROTOCOL_CLIENT_CONNECTION_AUTH, 
-                     &sock->protocol, (void *)proto_ctx, 
+  /* 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);
+}
+
+/* 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(bool success,
+                                    SilcProtocolAuthMeth auth_meth,
+                                    const unsigned char *auth_data,
+                                    SilcUInt32 auth_data_len, 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;
+  }
+
+  /* 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);
 
   /* Execute the protocol */
-  sock->protocol->execute(client->timeout_queue, 0, sock->protocol, fd, 0, 0);
+  silc_protocol_execute(proto_ctx->sock->protocol, client->schedule, 0, 0);
 }
 
 /* Finalizes the connection to the remote SILC server. This is called
@@ -442,7 +776,7 @@ SILC_TASK_CALLBACK(silc_client_connect_to_server_second)
 SILC_TASK_CALLBACK(silc_client_connect_to_server_final)
 {
   SilcProtocol protocol = (SilcProtocol)context;
-  SilcClientConnAuthInternalContext *ctx = 
+  SilcClientConnAuthInternalContext *ctx =
     (SilcClientConnAuthInternalContext *)protocol->context;
   SilcClient client = (SilcClient)ctx->client;
   SilcClientConnection conn = (SilcClientConnection)ctx->sock->user_data;
@@ -450,84 +784,146 @@ SILC_TASK_CALLBACK(silc_client_connect_to_server_final)
 
   SILC_LOG_DEBUG(("Start"));
 
-  if (protocol->state == SILC_PROTOCOL_STATE_ERROR) {
+  if (protocol->state == SILC_PROTOCOL_STATE_ERROR ||
+      protocol->state == SILC_PROTOCOL_STATE_FAILURE) {
     /* Error occured during protocol */
     SILC_LOG_DEBUG(("Error during authentication protocol"));
-    silc_protocol_free(protocol);
-    if (ctx->auth_data)
-      silc_free(ctx->auth_data);
-    if (ctx->ske)
-      silc_ske_free(ctx->ske);
-    if (ctx->dest_id)
-      silc_free(ctx->dest_id);
-    conn->sock->protocol = NULL;
-
-    /* Notify application of failure */
-    client->ops->connect(client, ctx->sock->user_data, FALSE);
-    silc_free(ctx);
-    return;
+    goto err;
   }
 
-  /* 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);
+  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))
+      goto err;
+
+    old_client_id = silc_id_str2id(old_id, old_id_len, SILC_ID_CLIENT);
+    if (!old_client_id) {
+      silc_free(old_id);
+      goto err;
+    }
 
-  /* 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);
+    /* 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);
+      goto err;
+    }
+
+    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);
+  }
 
   /* 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_SERVER_LEN;
+  conn->remote_id_data_len = silc_id_get_len(ctx->dest_id, SILC_ID_SERVER);
 
-  /* Notify application of successful connection */
-  client->ops->connect(client, conn, TRUE);
+  /* 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);
-  if (ctx->auth_data)
-    silc_free(ctx->auth_data);
+  silc_free(ctx->auth_data);
   if (ctx->ske)
     silc_ske_free(ctx->ske);
-  if (ctx->dest_id)
-    silc_free(ctx->dest_id);
+  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);
 }
 
 /* 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. */
 
-static int silc_client_packet_send_real(SilcClient client,
-                                       SilcSocketConnection sock,
-                                       int force_send)
+int silc_client_packet_send_real(SilcClient client,
+                                SilcSocketConnection sock,
+                                bool 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. 
+  /* 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). 
+     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(sock->sock);
+  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
@@ -540,7 +936,7 @@ static int silc_client_packet_send_real(SilcClient client,
 /* Packet processing callback. This is used to send and receive packets
    from network. This is generic task. */
 
-SILC_TASK_CALLBACK(silc_client_packet_process)
+SILC_TASK_CALLBACK_GLOBAL(silc_client_packet_process)
 {
   SilcClient client = (SilcClient)context;
   SilcSocketConnection sock = NULL;
@@ -557,24 +953,26 @@ SILC_TASK_CALLBACK(silc_client_packet_process)
 
   /* Packet sending */
   if (type == SILC_TASK_WRITE) {
-    SILC_LOG_DEBUG(("Writing data to connection"));
-
-    if (sock->outbuf->data - sock->outbuf->head)
-      silc_buffer_push(sock->outbuf, 
-                      sock->outbuf->data - sock->outbuf->head);
+    /* Do not send data to disconnected connection */
+    if (SILC_IS_DISCONNECTED(sock))
+      return;
 
-    ret = silc_client_packet_send_real(client, sock, TRUE);
+    ret = silc_packet_send(sock, TRUE);
 
     /* If returned -2 could not write to connection now, will do
        it later. */
     if (ret == -2)
       return;
-    
+
+    /* Error */
+    if (ret == -1)
+      return;
+
     /* 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. 
+       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(fd);
+    SILC_CLIENT_SET_CONNECTION_FOR_INPUT(client->schedule, fd);
     SILC_UNSET_OUTBUF_PENDING(sock);
 
     silc_buffer_clear(sock->outbuf);
@@ -583,13 +981,11 @@ SILC_TASK_CALLBACK(silc_client_packet_process)
 
   /* Packet receiving */
   if (type == SILC_TASK_READ) {
-    SILC_LOG_DEBUG(("Reading data from connection"));
-
     /* Read data from network */
     ret = silc_packet_receive(sock);
     if (ret < 0)
       return;
-    
+
     /* EOF */
     if (ret == 0) {
       SILC_LOG_DEBUG(("Read EOF"));
@@ -597,138 +993,138 @@ SILC_TASK_CALLBACK(silc_client_packet_process)
       /* If connection is disconnecting already we will finally
         close the connection */
       if (SILC_IS_DISCONNECTING(sock)) {
-       client->ops->disconnect(client, conn);
-       silc_client_close_connection(client, 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;
       }
-      
-      client->ops->say(client, conn, "Connection closed: premature EOF");
-      SILC_LOG_DEBUG(("Premature EOF from connection %d", sock->sock));
-      client->ops->disconnect(client, conn);
-      silc_client_close_connection(client, sock);
+
+      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. */
-    silc_packet_receive_process(sock, conn->receive_key, conn->hmac,
-                               silc_client_packet_parse, client);
+    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);
   }
 }
 
-/* Parses whole packet, received earlier. */
+/* Parser callback called by silc_packet_receive_process. Thie merely
+   registers timeout that will handle the actual parsing when appropriate. */
 
-SILC_TASK_CALLBACK(silc_client_packet_parse_real)
+static bool silc_client_packet_parse(SilcPacketParserContext *parser_context,
+                                    void *context)
 {
-  SilcPacketParserContext *parse_ctx = (SilcPacketParserContext *)context;
-  SilcClient client = (SilcClient)parse_ctx->context;
-  SilcPacketContext *packet = parse_ctx->packet;
-  SilcBuffer buffer = packet->buffer;
-  SilcSocketConnection sock = parse_ctx->sock;
+  SilcClient client = (SilcClient)context;
+  SilcSocketConnection sock = parser_context->sock;
   SilcClientConnection conn = (SilcClientConnection)sock->user_data;
-  int ret;
+  SilcPacketContext *packet = parser_context->packet;
+  SilcPacketType ret;
 
-  SILC_LOG_DEBUG(("Start"));
+  if (conn && conn->internal->hmac_receive && conn->sock == sock)
+    conn->internal->psn_receive = parser_context->packet->sequence + 1;
 
-  /* Decrypt the received packet */
-  ret = silc_packet_decrypt(conn->receive_key, conn->hmac, buffer, packet);
-  if (ret < 0)
-    goto out;
+  /* 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 == 0) {
-    /* Parse the packet. Packet type is returned. */
-    ret = silc_packet_parse(packet);
-  } else {
-    /* Parse the packet header in special way as this is "special"
-       packet type. */
-    ret = silc_packet_parse_special(packet);
+  if (ret == SILC_PACKET_NONE) {
+    silc_packet_context_free(packet);
+    silc_free(parser_context);
+    return FALSE;
   }
 
-  if (ret == SILC_PACKET_NONE)
-    goto out;
+  /* 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);
+    silc_packet_context_free(packet);
+    silc_free(parser_context);
+
+    /* 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);
+
+    return FALSE;
+  }
 
   /* Parse the incoming packet type */
   silc_client_packet_parse_type(client, sock, packet);
-
- out:
-  silc_buffer_clear(sock->inbuf);
   silc_packet_context_free(packet);
-  silc_free(parse_ctx);
+  silc_free(parser_context);
+  return TRUE;
 }
 
-/* Parser callback called by silc_packet_receive_process. Thie merely
-   registers timeout that will handle the actual parsing when appropriate. */
-
-void silc_client_packet_parse(SilcPacketParserContext *parser_context)
-{
-  SilcClient client = (SilcClient)parser_context->context;
-
-  /* Parse the packet */
-  silc_task_register(client->timeout_queue, parser_context->sock->sock, 
-                    silc_client_packet_parse_real,
-                    (void *)parser_context, 0, 1, 
-                    SILC_TASK_TIMEOUT,
-                    SILC_TASK_PRI_NORMAL);
-}
-  
 /* 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, 
+void silc_client_packet_parse_type(SilcClient client,
                                   SilcSocketConnection sock,
                                   SilcPacketContext *packet)
 {
   SilcBuffer buffer = packet->buffer;
   SilcPacketType type = packet->type;
 
-  SILC_LOG_DEBUG(("Parsing packet type %d", 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) {
-      sock->protocol->execute(client->timeout_queue, 0,
-                             sock->protocol, sock->sock, 0, 0);
-    }
+    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 
+     * 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.
      */
-    if (sock->protocol) {
-      sock->protocol->state = SILC_PROTOCOL_STATE_FAILURE;
-      sock->protocol->execute(client->timeout_queue, 0,
-                             sock->protocol, sock->sock, 0, 0);
-
-      /* XXX We have only two protocols currently thus we know what this
-        failure indication is. */
-      if (buffer->len >= 4) {
-       unsigned int failure;
-
-       SILC_GET32_MSB(failure, buffer->data);
-
-       /* Notify application */
-       client->ops->failure(client, sock->user_data, sock->protocol,
-                            (void *)failure);
-      }
-    }
+    silc_client_process_failure(client, sock, packet);
     break;
+
   case SILC_PACKET_REJECT:
     break;
 
   case SILC_PACKET_NOTIFY:
     /*
-     * Received notify message 
+     * Received notify message
      */
     silc_client_notify_by_server(client, sock, packet);
     break;
@@ -746,6 +1142,7 @@ void silc_client_packet_parse_type(SilcClient client,
      */
     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
@@ -761,12 +1158,21 @@ void silc_client_packet_parse_type(SilcClient client,
      */
     silc_client_private_message(client, sock, packet);
     break;
+
   case SILC_PACKET_PRIVATE_MESSAGE_KEY:
     /*
      * Received private message key
      */
     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
@@ -775,48 +1181,99 @@ void silc_client_packet_parse_type(SilcClient client,
     break;
 
   case SILC_PACKET_KEY_EXCHANGE:
-    if (sock->protocol) {
-      SilcClientKEInternalContext *proto_ctx = 
+    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_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 */
-      sock->protocol->execute(client->timeout_queue, 0,
-                             sock->protocol, sock->sock, 0, 0);
+      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."));
-
-      /* XXX Trigger KE protocol?? Rekey actually! */
     }
     break;
 
   case SILC_PACKET_KEY_EXCHANGE_1:
-    if (sock->protocol) {
+    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) {
-      SilcClientKEInternalContext *proto_ctx = 
-       (SilcClientKEInternalContext *)sock->protocol->context;
+    if (sock->protocol && sock->protocol->protocol &&
+       (sock->protocol->protocol->type == SILC_PROTOCOL_CLIENT_KEY_EXCHANGE ||
+        sock->protocol->protocol->type == SILC_PROTOCOL_CLIENT_REKEY)) {
 
-      if (proto_ctx->packet)
-       silc_packet_context_free(proto_ctx->packet);
+      if (sock->protocol->protocol->type == SILC_PROTOCOL_CLIENT_REKEY) {
+       SilcClientRekeyInternalContext *proto_ctx =
+         (SilcClientRekeyInternalContext *)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_type);
+       if (proto_ctx->packet)
+         silc_packet_context_free(proto_ctx->packet);
 
-      /* Let the protocol handle the packet */
-      sock->protocol->execute(client->timeout_queue, 0,
-                             sock->protocol, sock->sock, 0, 0);
+       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."));
@@ -827,13 +1284,15 @@ void silc_client_packet_parse_type(SilcClient client,
     {
       /*
        * Received new ID from server. This packet is received at
-       * the connection to the server.  New ID is also received when 
+       * 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);
+      idp = silc_id_payload_parse(buffer->data, buffer->len);
+      if (!idp)
+       break;
       if (silc_id_payload_get_type(idp) != SILC_ID_CLIENT)
        break;
 
@@ -842,6 +1301,67 @@ void silc_client_packet_parse_type(SilcClient client,
       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;
@@ -854,399 +1374,262 @@ void silc_client_packet_parse_type(SilcClient client,
    will be derived from sock argument. Otherwise the valid arguments sent
    are used. */
 
-void silc_client_packet_send(SilcClient client, 
+void silc_client_packet_send(SilcClient client,
                             SilcSocketConnection sock,
-                            SilcPacketType type, 
+                            SilcPacketType type,
                             void *dst_id,
                             SilcIdType dst_id_type,
                             SilcCipher cipher,
                             SilcHmac hmac,
-                            unsigned char *data, 
-                            unsigned int data_len, 
-                            int force_send)
+                            unsigned char *data,
+                            SilcUInt32 data_len,
+                            bool 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)->send_key)
-      cipher = ((SilcClientConnection)sock->user_data)->send_key;
+    if (!cipher && ((SilcClientConnection)sock->user_data)->internal->send_key)
+      cipher = ((SilcClientConnection)sock->user_data)->internal->send_key;
 
-    if (!hmac && ((SilcClientConnection)sock->user_data)->hmac)
-      hmac = ((SilcClientConnection)sock->user_data)->hmac;
+    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;
     }
+
+    if (hmac)
+      sequence = ((SilcClientConnection)sock->user_data)->internal->psn_send++;
+
+    /* 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);
   }
 
+  block_len = cipher ? silc_cipher_get_block_len(cipher) : 0;
+
   /* Set the packet context pointers */
   packetdata.flags = 0;
   packetdata.type = type;
-  if (((SilcClientConnection)sock->user_data)->local_id_data)
+  if (sock->user_data &&
+      ((SilcClientConnection)sock->user_data)->local_id_data) {
     packetdata.src_id = ((SilcClientConnection)sock->user_data)->local_id_data;
-  else 
+    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_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_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;
   }
-  packetdata.rng = client->rng;
-  packetdata.truelen = data_len + SILC_PACKET_HEADER_LEN + 
+  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;
-  packetdata.padlen = SILC_PACKET_PADLEN(packetdata.truelen);
-
-  /* Prepare outgoing data buffer for packet sending */
-  silc_packet_send_prepare(sock, 
-                          SILC_PACKET_HEADER_LEN +
-                          packetdata.src_id_len + 
-                          packetdata.dst_id_len,
-                          packetdata.padlen,
-                          data_len);
-
-  SILC_LOG_DEBUG(("Putting data to outgoing buffer, len %d", data_len));
-
-  packetdata.buffer = sock->outbuf;
-
-  /* Put the data to the buffer */
-  if (data && data_len)
-    silc_buffer_put(sock->outbuf, data, data_len);
+  if (type == SILC_PACKET_CONNECTION_AUTH)
+    SILC_PACKET_PADLEN_MAX(packetdata.truelen, block_len, packetdata.padlen);
+  else
+    SILC_PACKET_PADLEN(packetdata.truelen, block_len, packetdata.padlen);
 
   /* Create the outgoing packet */
-  silc_packet_assemble(&packetdata);
+  if (!silc_packet_assemble(&packetdata, client->rng, cipher, hmac, sock,
+                            data, data_len, (const SilcBuffer)&packet)) {
+    SILC_LOG_ERROR(("Error assembling packet"));
+    return;
+  }
 
   /* Encrypt the packet */
   if (cipher)
-    silc_packet_encrypt(cipher, hmac, sock->outbuf, sock->outbuf->len);
+    silc_packet_encrypt(cipher, hmac, sequence, (SilcBuffer)&packet,
+                        packet.len);
 
-  SILC_LOG_HEXDUMP(("Packet, len %d", sock->outbuf->len),
-                  sock->outbuf->data, sock->outbuf->len);
+  SILC_LOG_HEXDUMP(("Packet (%d), len %d", sequence, packet.len),
+                  packet.data, packet.len);
 
   /* Now actually send the packet */
   silc_client_packet_send_real(client, sock, force_send);
 }
 
-/* Sends packet to a channel. Packet to channel is always encrypted
-   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. */
+/* Packet sending routine for application.  This is the only routine that
+   is provided for application to send SILC packets. */
 
-void silc_client_packet_send_to_channel(SilcClient client, 
-                                       SilcSocketConnection sock,
-                                       SilcChannelEntry channel,
-                                       unsigned char *data, 
-                                       unsigned int data_len, 
-                                       int force_send)
+bool silc_client_send_packet(SilcClient client,
+                            SilcClientConnection conn,
+                            SilcPacketType type,
+                            const unsigned char *data,
+                            SilcUInt32 data_len)
 {
-  int i;
-  SilcClientConnection conn = (SilcClientConnection)sock->user_data;
-  SilcBuffer payload;
-  SilcPacketContext packetdata;
-  SilcCipher cipher;
-  SilcHmac hmac;
-  unsigned char *id_string;
-
-  SILC_LOG_DEBUG(("Sending packet to channel"));
-
-  if (!channel || !channel->key) {
-    client->ops->say(client, conn, 
-                    "Cannot talk to channel: key does not exist");
-    return;
-  }
-
-  /* Generate IV */
-  if (!channel->iv)
-    for (i = 0; i < 16; i++) channel->iv[i] = silc_rng_get_byte(client->rng);
-  else
-    silc_hash_make(client->md5hash, channel->iv, 16, channel->iv);
-
-  /* Encode the channel payload */
-  payload = silc_channel_payload_encode(data_len, data, 16, channel->iv, 
-                                       client->rng);
-  if (!payload) {
-    client->ops->say(client, conn, 
-                    "Error: Could not create packet to be sent to channel");
-    return;
-  }
-
-  /* Get data used in packet header encryption, keys and stuff. Rest
-     of the packet (the payload) is, however, encrypted with the 
-     specified channel key. */
-  cipher = conn->send_key;
-  hmac = conn->hmac;
-  id_string = silc_id_id2str(channel->id, SILC_ID_CHANNEL);
-
-  /* Set the packet context pointers. The destination ID is always
-     the Channel ID of the channel. Server and router will handle the
-     distribution of the packet. */
-  packetdata.flags = 0;
-  packetdata.type = SILC_PACKET_CHANNEL_MESSAGE;
-  packetdata.src_id = conn->local_id_data;
-  packetdata.src_id_len = SILC_ID_CLIENT_LEN;
-  packetdata.src_id_type = SILC_ID_CLIENT;
-  packetdata.dst_id = id_string;
-  packetdata.dst_id_len = SILC_ID_CHANNEL_LEN;
-  packetdata.dst_id_type = SILC_ID_CHANNEL;
-  packetdata.rng = client->rng;
-  packetdata.truelen = payload->len + SILC_PACKET_HEADER_LEN + 
-    packetdata.src_id_len + packetdata.dst_id_len;
-  packetdata.padlen = SILC_PACKET_PADLEN((SILC_PACKET_HEADER_LEN +
-                                         packetdata.src_id_len +
-                                         packetdata.dst_id_len));
-
-  /* Prepare outgoing data buffer for packet sending */
-  silc_packet_send_prepare(sock, 
-                          SILC_PACKET_HEADER_LEN +
-                          packetdata.src_id_len + 
-                          packetdata.dst_id_len,
-                          packetdata.padlen,
-                          payload->len);
 
-  packetdata.buffer = sock->outbuf;
-
-  /* Encrypt payload of the packet. This is encrypted with the channel key. */
-  channel->channel_key->cipher->encrypt(channel->channel_key->context,
-                                       payload->data, payload->data,
-                                       payload->len - 16, /* -IV_LEN */
-                                       channel->iv);
-
-  /* Put the actual encrypted payload data into the buffer. */
-  silc_buffer_put(sock->outbuf, payload->data, payload->len);
-
-  /* Create the outgoing packet */
-  silc_packet_assemble(&packetdata);
-
-  /* Encrypt the header and padding of the packet. This is encrypted 
-     with normal session key shared with our server. */
-  silc_packet_encrypt(cipher, hmac, sock->outbuf, SILC_PACKET_HEADER_LEN + 
-                     packetdata.src_id_len + packetdata.dst_id_len +
-                     packetdata.padlen);
-
-  SILC_LOG_HEXDUMP(("Packet to channel, len %d", sock->outbuf->len),
-                  sock->outbuf->data, sock->outbuf->len);
+  assert(client);
+  if (!conn)
+    return FALSE;
 
-  /* Now actually send the packet */
-  silc_client_packet_send_real(client, sock, force_send);
-  silc_buffer_free(payload);
-  silc_free(id_string);
+  silc_client_packet_send(client, conn->sock, type, NULL, 0, NULL, NULL,
+                         (unsigned char *)data, data_len, TRUE);
+  return TRUE;
 }
 
-/* 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. */
-
-void silc_client_packet_send_private_message(SilcClient client,
-                                            SilcSocketConnection sock,
-                                            SilcClientEntry client_entry,
-                                            unsigned char *data, 
-                                            unsigned int data_len, 
-                                            int force_send)
+void silc_client_packet_queue_purge(SilcClient client,
+                                   SilcSocketConnection sock)
 {
-  SilcClientConnection conn = (SilcClientConnection)sock->user_data;
-  SilcBuffer buffer;
-  SilcPacketContext packetdata;
-  unsigned int nick_len;
-  SilcCipher cipher;
-  SilcHmac hmac;
-
-  SILC_LOG_DEBUG(("Sending private message"));
-
-  /* Create private message payload */
-  nick_len = strlen(conn->nickname);
-  buffer = silc_buffer_alloc(2 + nick_len + data_len);
-  silc_buffer_pull_tail(buffer, SILC_BUFFER_END(buffer));
-  silc_buffer_format(buffer,
-                    SILC_STR_UI_SHORT(nick_len),
-                    SILC_STR_UI_XNSTRING(conn->nickname,
-                                         nick_len),
-                    SILC_STR_UI_XNSTRING(data, data_len),
-                    SILC_STR_END);
-
-  /* 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);
-    goto out;
+  if (sock && SILC_IS_OUTBUF_PENDING(sock) &&
+      (SILC_IS_DISCONNECTED(sock) == FALSE)) {
+    silc_packet_send(sock, TRUE);
+    SILC_UNSET_OUTBUF_PENDING(sock);
+    SILC_CLIENT_SET_CONNECTION_FOR_INPUT(client->schedule, sock->sock);
+    silc_buffer_clear(sock->outbuf);
   }
+}
 
-  /* We have private message specific key */
-
-  /* Get data used in the encryption */
-  cipher = client_entry->send_key;
-  hmac = conn->hmac;
-
-  /* Set the packet context pointers. */
-  packetdata.flags = 0;
-  packetdata.type = SILC_PACKET_PRIVATE_MESSAGE;
-  packetdata.src_id = conn->local_id_data;
-  packetdata.src_id_len = SILC_ID_CLIENT_LEN;
-  packetdata.src_id_type = SILC_ID_CLIENT;
-  if (client_entry)
-    packetdata.dst_id = silc_id_id2str(client_entry->id, SILC_ID_CLIENT);
-  else
-    packetdata.dst_id = conn->local_id_data;
-  packetdata.dst_id_len = SILC_ID_CLIENT_LEN;
-  packetdata.dst_id_type = SILC_ID_CLIENT;
-  packetdata.rng = client->rng;
-  packetdata.truelen = buffer->len + SILC_PACKET_HEADER_LEN + 
-    packetdata.src_id_len + packetdata.dst_id_len;
-  packetdata.padlen = SILC_PACKET_PADLEN((SILC_PACKET_HEADER_LEN +
-                                         packetdata.src_id_len +
-                                         packetdata.dst_id_len));
-
-  /* Prepare outgoing data buffer for packet sending */
-  silc_packet_send_prepare(sock, 
-                          SILC_PACKET_HEADER_LEN +
-                          packetdata.src_id_len + 
-                          packetdata.dst_id_len,
-                          packetdata.padlen,
-                          buffer->len);
-  
-  packetdata.buffer = sock->outbuf;
-
-  /* Encrypt payload of the packet. Encrypt with private message specific
-     key if it exist, otherwise with session key. */
-  cipher->cipher->encrypt(cipher->context, buffer->data, buffer->data,
-                         buffer->len, cipher->iv);
-      
-  /* Put the actual encrypted payload data into the buffer. */
-  silc_buffer_put(sock->outbuf, buffer->data, buffer->len);
-
-  /* Create the outgoing packet */
-  silc_packet_assemble(&packetdata);
-
-  /* Encrypt the header and padding of the packet. */
-  silc_packet_encrypt(cipher, hmac, sock->outbuf, SILC_PACKET_HEADER_LEN + 
-                     packetdata.src_id_len + packetdata.dst_id_len +
-                     packetdata.padlen);
-
-  SILC_LOG_HEXDUMP(("Private message packet, len %d", sock->outbuf->len),
-                  sock->outbuf->data, sock->outbuf->len);
+/* 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'). */
+
+void silc_client_close_connection_real(SilcClient client,
+                                      SilcSocketConnection sock,
+                                      SilcClientConnection conn)
+{
+  int del = FALSE;
 
-  /* Now actually send the packet */
-  silc_client_packet_send_real(client, sock, force_send);
-  silc_free(packetdata.dst_id);
+  SILC_LOG_DEBUG(("Start"));
 
- out:
-  silc_free(buffer);
-}     
+  if (!sock && !conn)
+    return;
 
-/* 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 (!sock || (sock && conn->sock == sock))
+    del = TRUE;
+  if (!sock)
+    sock = conn->sock;
 
-void silc_client_close_connection(SilcClient client,
-                                 SilcSocketConnection sock)
-{
-  SilcClientConnection conn;
+  if (!sock) {
+    if (del && conn)
+      silc_client_del_connection(client, conn);
+    return;
+  }
 
   /* We won't listen for this connection anymore */
-  silc_schedule_unset_listen_fd(sock->sock);
+  silc_schedule_unset_listen_fd(client->schedule, sock->sock);
 
   /* Unregister all tasks */
-  silc_task_unregister_by_fd(client->io_queue, sock->sock);
-  silc_task_unregister_by_fd(client->timeout_queue, sock->sock);
+  silc_schedule_task_del_by_fd(client->schedule, sock->sock);
 
   /* Close the actual connection */
   silc_net_close_connection(sock->sock);
 
-  client->ops->say(client, sock->user_data,
-                  "Closed connection to host %s", sock->hostname ?
-                  sock->hostname : sock->ip);
+  /* 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;
+    }
+  }
 
   /* Free everything */
-  if (sock->user_data) {
-    conn = (SilcClientConnection)sock->user_data;
+  if (del && sock->user_data)
+    silc_client_del_connection(client, conn);
 
-    /* XXX Free all client entries and channel entries. */
+  silc_socket_free(sock);
+}
 
-    /* Clear ID caches */
-    silc_idcache_del_all(conn->client_cache);
-    silc_idcache_del_all(conn->channel_cache);
+/* Closes the connection to the remote end */
 
-    /* Free data */
-    if (conn->remote_host)
-      silc_free(conn->remote_host);
-    if (conn->local_id)
-      silc_free(conn->local_id);
-    if (conn->local_id_data)
-      silc_free(conn->local_id_data);
-    if (conn->send_key)
-      silc_cipher_free(conn->send_key);
-    if (conn->receive_key)
-      silc_cipher_free(conn->receive_key);
-    if (conn->hmac)
-      silc_hmac_free(conn->hmac);
-    if (conn->hmac_key) {
-      memset(conn->hmac_key, 0, conn->hmac_key_len);
-      silc_free(conn->hmac_key);
-    }
-    if (conn->pending_commands)
-      silc_dlist_uninit(conn->pending_commands);
-
-    conn->sock = NULL;
-    conn->remote_port = 0;
-    conn->remote_type = 0;
-    conn->send_key = NULL;
-    conn->receive_key = NULL;
-    conn->hmac = NULL;
-    conn->hmac_key = NULL;
-    conn->hmac_key_len = 0;
-    conn->local_id = NULL;
-    conn->local_id_data = NULL;
-    conn->remote_host = NULL;
-    conn->current_channel = NULL;
-    conn->pending_commands = NULL;
+void silc_client_close_connection(SilcClient client,
+                                 SilcClientConnection conn)
+{
+  silc_client_close_connection_real(client, NULL, conn);
+}
 
-    silc_client_del_connection(client, conn);
-  }
+/* Called when we receive disconnection packet from server. This
+   closes our end properly and displays the reason of the disconnection
+   on the screen. */
 
-  if (sock->protocol) {
-    silc_protocol_free(sock->protocol);
-    sock->protocol = NULL;
-  }
-  silc_socket_free(sock);
+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);
 }
 
-/* Called when we receive disconnection packet from server. This 
+/* 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 message)
+                                       SilcBuffer packet)
 {
-  char *msg;
+  SilcClientConnection conn;
+  SilcStatus status;
+  char *message = NULL;
 
   SILC_LOG_DEBUG(("Server disconnected us, sock %d", sock->sock));
 
-  msg = silc_calloc(message->len + 1, sizeof(char));
-  memcpy(msg, message->data, message->len);
-  client->ops->say(client, sock->user_data, msg);
-  silc_free(msg);
+  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);
-  silc_client_close_connection(client, 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. 
+/* 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,
@@ -1255,468 +1638,52 @@ void silc_client_error_by_server(SilcClient client,
 {
   char *msg;
 
-  msg = silc_calloc(message->len + 1, sizeof(char));
-  memcpy(msg, message->data, message->len);
-  client->ops->say(client, sock->user_data, msg);
+  msg = silc_memdup(message->data, message->len);
+  client->internal->ops->say(client, sock->user_data,
+                            SILC_CLIENT_MESSAGE_AUDIT, msg);
   silc_free(msg);
 }
 
-/* Called when notify is received and some async operation (such as command)
-   is required before processing the notify message. This calls again the
-   silc_client_notify_by_server and reprocesses the original notify packet. */
+/* Auto-nicking callback to send NICK command to server. */
 
-static void silc_client_notify_by_server_pending(void *context)
+SILC_TASK_CALLBACK(silc_client_send_auto_nick)
 {
-  SilcPacketContext *p = (SilcPacketContext *)context;
-  silc_client_notify_by_server(p->context, p->sock, p);
-  silc_packet_context_free(p);
+  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));
 }
 
-/* Received notify message from server */
+/* 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. */
 
-void silc_client_notify_by_server(SilcClient client,
-                                 SilcSocketConnection sock,
-                                 SilcPacketContext *packet)
+static void silc_client_resume_session_cb(SilcClient client,
+                                         SilcClientConnection conn,
+                                         bool success,
+                                         void *context)
 {
-  SilcBuffer buffer = packet->buffer;
-  SilcClientConnection conn = (SilcClientConnection)sock->user_data;
-  SilcNotifyPayload payload;
-  SilcNotifyType type;
-  SilcArgumentPayload args;
-
-  SilcClientID *client_id = NULL;
-  SilcChannelID *channel_id = NULL;
-  SilcClientEntry client_entry;
-  SilcClientEntry client_entry2;
-  SilcChannelEntry channel;
-  SilcChannelUser chu;
-  SilcIDCacheEntry id_cache = NULL;
-  unsigned char *tmp;
-  unsigned int tmp_len, mode;
-
-  payload = silc_notify_payload_parse(buffer);
-  type = silc_notify_get_type(payload);
-  args = silc_notify_get_args(payload);
-  if (!args)
-    goto out;
-
-  switch(type) {
-  case SILC_NOTIFY_TYPE_NONE:
-    /* Notify application */
-    client->ops->notify(client, conn, type, 
-                       silc_argument_get_arg_type(args, 1, NULL));
-    break;
-
-  case SILC_NOTIFY_TYPE_INVITE:
-    /* 
-     * Someone invited me to a channel. Find Client and Channel entries
-     * for the application.
-     */
-    
-    /* Get Client ID */
-    tmp = silc_argument_get_arg_type(args, 1, &tmp_len);
-    if (!tmp)
-      goto out;
-
-    client_id = silc_id_payload_parse_id(tmp, tmp_len);
-
-    /* Find Client entry and if not found query it */
-    client_entry = silc_idlist_get_client_by_id(client, conn, client_id, TRUE);
-    if (!client_entry) {
-      SilcPacketContext *p = silc_packet_context_dup(packet);
-      p->context = (void *)client;
-      p->sock = sock;
-      silc_client_command_pending(conn,SILC_COMMAND_WHOIS, 0, 
-                                 silc_client_notify_by_server_pending, p);
-      goto out;
-    }
-
-    /* Get Channel ID */
-    tmp = silc_argument_get_arg_type(args, 2, &tmp_len);
-    if (!tmp)
-      goto out;
-
-    channel_id = silc_id_payload_parse_id(tmp, tmp_len);
-
-    /* XXX Will ALWAYS fail because currently we don't have way to resolve
-       channel information for channel that we're not joined to. */
-    /* XXX ways to fix: use (extended) LIST command, or define the channel
-       name to the notfy type when name resolving is not mandatory. */
-    /* Find channel entry */
-    if (!silc_idcache_find_by_id_one(conn->channel_cache, (void *)channel_id,
-                                    SILC_ID_CHANNEL, &id_cache))
-      goto out;
-
-    channel = (SilcChannelEntry)id_cache->context;
-
-    /* Notify application */
-    client->ops->notify(client, conn, type, client_entry, channel);
-    break;
-
-  case SILC_NOTIFY_TYPE_JOIN:
-    /*
-     * Someone has joined to a channel. Get their ID and nickname and
-     * cache them for later use.
-     */
-
-    /* Get Client ID */
-    tmp = silc_argument_get_arg_type(args, 1, &tmp_len);
-    if (!tmp)
-      goto out;
-
-    client_id = silc_id_payload_parse_id(tmp, tmp_len);
-
-    /* Find Client entry and if not found query it */
-    client_entry = silc_idlist_get_client_by_id(client, conn, client_id, TRUE);
-    if (!client_entry) {
-      SilcPacketContext *p = silc_packet_context_dup(packet);
-      p->context = (void *)client;
-      p->sock = sock;
-      silc_client_command_pending(conn, SILC_COMMAND_WHOIS, 0, 
-                                 silc_client_notify_by_server_pending, p);
-      goto out;
-    }
-
-    /* Get channel entry */
-    channel_id = silc_id_str2id(packet->dst_id, SILC_ID_CHANNEL);
-    if (!silc_idcache_find_by_id_one(conn->channel_cache, (void *)channel_id,
-                                    SILC_ID_CHANNEL, &id_cache))
-      break;
-
-    channel = (SilcChannelEntry)id_cache->context;
-
-    /* Add client to channel */
-    chu = silc_calloc(1, sizeof(*chu));
-    chu->client = client_entry;
-    silc_list_add(channel->clients, chu);
-
-    /* XXX add support for multiple same nicks on same channel. Check
-       for them here */
-
-    /* Notify application. The channel entry is sent last as this notify
-       is for channel but application don't know it from the arguments
-       sent by server. */
-    client->ops->notify(client, conn, type, client_entry, channel);
-    break;
-
-  case SILC_NOTIFY_TYPE_LEAVE:
-    /*
-     * Someone has left a channel. We will remove it from the channel but
-     * we'll keep it in the cache in case we'll need it later.
-     */
-    
-    /* Get Client ID */
-    tmp = silc_argument_get_arg_type(args, 1, &tmp_len);
-    if (!tmp)
-      goto out;
-
-    client_id = silc_id_payload_parse_id(tmp, tmp_len);
-
-    /* Find Client entry */
-    client_entry = 
-      silc_idlist_get_client_by_id(client, conn, client_id, FALSE);
-    if (!client_entry)
-      goto out;
-
-    /* Get channel entry */
-    channel_id = silc_id_str2id(packet->dst_id, SILC_ID_CHANNEL);
-    if (!silc_idcache_find_by_id_one(conn->channel_cache, (void *)channel_id,
-                                    SILC_ID_CHANNEL, &id_cache))
-      break;
-
-    channel = (SilcChannelEntry)id_cache->context;
-
-    /* Remove client from channel */
-    silc_list_start(channel->clients);
-    while ((chu = silc_list_get(channel->clients)) != SILC_LIST_END) {
-      if (chu->client == client_entry) {
-       silc_list_del(channel->clients, chu);
-       silc_free(chu);
-       break;
-      }
-    }
-
-    /* Notify application. The channel entry is sent last as this notify
-       is for channel but application don't know it from the arguments
-       sent by server. */
-    client->ops->notify(client, conn, type, client_entry, channel);
-    break;
-
-  case SILC_NOTIFY_TYPE_SIGNOFF:
-    /*
-     * Someone left SILC. We'll remove it from all channels and from cache.
-     */
-
-    /* Get Client ID */
-    tmp = silc_argument_get_arg_type(args, 1, &tmp_len);
-    if (!tmp)
-      goto out;
-
-    client_id = silc_id_payload_parse_id(tmp, tmp_len);
-
-    /* Find Client entry */
-    client_entry = 
-      silc_idlist_get_client_by_id(client, conn, client_id, FALSE);
-    if (!client_entry)
-      goto out;
-
-    /* Remove from all channels */
-    silc_client_remove_from_channels(client, conn, client_entry);
-
-    /* Remove from cache */
-    silc_idcache_del_by_id(conn->client_cache, SILC_ID_CLIENT, 
-                          client_entry->id);
-
-    /* Notify application */
-    client->ops->notify(client, conn, type, client_entry);
-
-    /* Free data */
-    if (client_entry->nickname)
-      silc_free(client_entry->nickname);
-    if (client_entry->server)
-      silc_free(client_entry->server);
-    if (client_entry->id)
-      silc_free(client_entry->id);
-    if (client_entry->send_key)
-      silc_cipher_free(client_entry->send_key);
-    if (client_entry->receive_key)
-      silc_cipher_free(client_entry->receive_key);
-    break;
-
-  case SILC_NOTIFY_TYPE_TOPIC_SET:
-    /*
-     * Someone set the topic on a channel.
-     */
-
-    /* Get Client ID */
-    tmp = silc_argument_get_arg_type(args, 1, &tmp_len);
-    if (!tmp)
-      goto out;
-
-    client_id = silc_id_payload_parse_id(tmp, tmp_len);
-
-    /* Find Client entry */
-    client_entry = 
-      silc_idlist_get_client_by_id(client, conn, client_id, FALSE);
-    if (!client_entry)
-      goto out;
-
-    /* Get topic */
-    tmp = silc_argument_get_arg_type(args, 2, &tmp_len);
-    if (!tmp)
-      goto out;
-
-    /* Get channel entry */
-    channel_id = silc_id_str2id(packet->dst_id, SILC_ID_CHANNEL);
-    if (!silc_idcache_find_by_id_one(conn->channel_cache, (void *)channel_id,
-                                    SILC_ID_CHANNEL, &id_cache))
-      break;
-
-    channel = (SilcChannelEntry)id_cache->context;
-
-    /* Notify application. The channel entry is sent last as this notify
-       is for channel but application don't know it from the arguments
-       sent by server. */
-    client->ops->notify(client, conn, type, client_entry, tmp, channel);
-    break;
-
-  case SILC_NOTIFY_TYPE_NICK_CHANGE:
-    /*
-     * Someone changed their nickname. If we don't have entry for the new
-     * ID we will query it and return here after it's done. After we've
-     * returned we fetch the old entry and free it and notify the 
-     * application.
-     */
-
-    /* Get new Client ID */
-    tmp = silc_argument_get_arg_type(args, 2, &tmp_len);
-    if (!tmp)
-      goto out;
-
-    client_id = silc_id_payload_parse_id(tmp, tmp_len);
-
-    /* Ignore my ID */
-    if (!SILC_ID_CLIENT_COMPARE(client_id, conn->local_id))
-      break;
-
-    /* Find Client entry and if not found query it */
-    client_entry2 = 
-      silc_idlist_get_client_by_id(client, conn, client_id, TRUE);
-    if (!client_entry2) {
-      SilcPacketContext *p = silc_packet_context_dup(packet);
-      p->context = (void *)client;
-      p->sock = sock;
-      silc_client_command_pending(conn, SILC_COMMAND_WHOIS, 0, 
-                                 silc_client_notify_by_server_pending, p);
-      goto out;
-    }
-
-    /* Get old Client ID */
-    tmp = silc_argument_get_arg_type(args, 1, &tmp_len);
-    if (!tmp)
-      goto out;
-
-    client_id = silc_id_payload_parse_id(tmp, tmp_len);
-
-    /* Find old Client entry */
-    client_entry = 
-      silc_idlist_get_client_by_id(client, conn, client_id, FALSE);
-    if (!client_entry)
-      goto out;
-
-    /* Remove the old from cache */
-    silc_idcache_del_by_id(conn->client_cache, SILC_ID_CLIENT, 
-                          client_entry->id);
-
-    /* Replace old ID entry with new one on all channels. */
-    silc_client_replace_from_channels(client, conn, client_entry,
-                                     client_entry2);
-
-    /* Notify application */
-    client->ops->notify(client, conn, type, client_entry, client_entry2);
-
-    /* Free data */
-    if (client_entry->nickname)
-      silc_free(client_entry->nickname);
-    if (client_entry->server)
-      silc_free(client_entry->server);
-    if (client_entry->id)
-      silc_free(client_entry->id);
-    if (client_entry->send_key)
-      silc_cipher_free(client_entry->send_key);
-    if (client_entry->receive_key)
-      silc_cipher_free(client_entry->receive_key);
-    break;
-
-  case SILC_NOTIFY_TYPE_CMODE_CHANGE:
-    /*
-     * Someone changed a channel mode
-     */
-
-    /* Get Client ID */
-    tmp = silc_argument_get_arg_type(args, 1, &tmp_len);
-    if (!tmp)
-      goto out;
-
-    client_id = silc_id_payload_parse_id(tmp, tmp_len);
-
-    /* Find Client entry */
-    client_entry = 
-      silc_idlist_get_client_by_id(client, conn, client_id, FALSE);
-    if (!client_entry)
-      goto out;
-
-    /* Get the mode */
-    tmp = silc_argument_get_arg_type(args, 2, &tmp_len);
-    if (!tmp)
-      goto out;
-
-    SILC_GET32_MSB(mode, tmp);
-
-    /* Get channel entry */
-    channel_id = silc_id_str2id(packet->dst_id, SILC_ID_CHANNEL);
-    if (!silc_idcache_find_by_id_one(conn->channel_cache, (void *)channel_id,
-                                    SILC_ID_CHANNEL, &id_cache))
-      break;
-
-    channel = (SilcChannelEntry)id_cache->context;
-
-    /* Save the new mode */
-    channel->mode = mode;
-
-    /* Notify application. The channel entry is sent last as this notify
-       is for channel but application don't know it from the arguments
-       sent by server. */
-    client->ops->notify(client, conn, type, client_entry, mode, channel);
-    break;
-
-  case SILC_NOTIFY_TYPE_CUMODE_CHANGE:
-    /*
-     * Someone changed user's mode on a channel
-     */
-
-    /* Get Client ID */
-    tmp = silc_argument_get_arg_type(args, 1, &tmp_len);
-    if (!tmp)
-      goto out;
-
-    client_id = silc_id_payload_parse_id(tmp, tmp_len);
-
-    /* Find Client entry */
-    client_entry = 
-      silc_idlist_get_client_by_id(client, conn, client_id, FALSE);
-    if (!client_entry)
-      goto out;
-
-    /* Get the mode */
-    tmp = silc_argument_get_arg_type(args, 2, &tmp_len);
-    if (!tmp)
-      goto out;
-
-    SILC_GET32_MSB(mode, tmp);
-
-    /* Get target Client ID */
-    tmp = silc_argument_get_arg_type(args, 3, &tmp_len);
-    if (!tmp)
-      goto out;
-
-    silc_free(client_id);
-    client_id = silc_id_payload_parse_id(tmp, tmp_len);
-
-    /* Find target Client entry */
-    client_entry2 = 
-      silc_idlist_get_client_by_id(client, conn, client_id, FALSE);
-    if (!client_entry2)
-      goto out;
-
-    /* Get channel entry */
-    channel_id = silc_id_str2id(packet->dst_id, SILC_ID_CHANNEL);
-    if (!silc_idcache_find_by_id_one(conn->channel_cache, (void *)channel_id,
-                                    SILC_ID_CHANNEL, &id_cache))
-      break;
-
-    channel = (SilcChannelEntry)id_cache->context;
-
-    /* Save the mode */
-    silc_list_start(channel->clients);
-    while ((chu = silc_list_get(channel->clients)) != SILC_LIST_END) {
-      if (chu->client == client_entry) {
-       chu->mode = mode;
-       break;
-      }
-    }
-
-    /* Notify application. The channel entry is sent last as this notify
-       is for channel but application don't know it from the arguments
-       sent by server. */
-    client->ops->notify(client, conn, type, client_entry, mode, 
-                       client_entry2, channel);
-    break;
-
-  case SILC_NOTIFY_TYPE_MOTD:
-    /*
-     * Received Message of the day
-     */
-
-    /* Get motd */
-    tmp = silc_argument_get_arg_type(args, 1, &tmp_len);
-    if (!tmp)
-      goto out;
-    
-    /* Notify application */
-    client->ops->notify(client, conn, type, tmp);
-    break;
-    
-  default:
-    break;
+  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);
+
+  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);
   }
-
- out:
-  silc_notify_payload_free(payload);
-  if (client_id)
-    silc_free(client_id);
-  if (channel_id)
-    silc_free(channel_id);
 }
 
 /* Processes the received new Client ID from server. Old Client ID is
@@ -1727,17 +1694,31 @@ void silc_client_receive_new_id(SilcClient client,
                                SilcIDPayload idp)
 {
   SilcClientConnection conn = (SilcClientConnection)sock->user_data;
+  int connecting = FALSE;
+  SilcClientID *client_id = silc_id_payload_get_id(idp);
+
+  if (!conn->local_entry)
+    connecting = TRUE;
 
   /* Delete old ID from ID cache */
-  silc_idcache_del_by_id(conn->client_cache, SILC_ID_CLIENT, conn->local_id);
-  
-  /* Save the new ID */
-  if (conn->local_id)
+  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 = silc_id_payload_get_id(idp);
+  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);;
 
@@ -1745,420 +1726,321 @@ void silc_client_receive_new_id(SilcClient client,
     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 = 
-      silc_calloc(strlen(client->username) + strlen(client->hostname) + 1,
-                 sizeof(conn->local_entry->username));
-    sprintf(conn->local_entry->username, "%s@%s", client->username,
-           client->hostname);
-  }
-  conn->local_entry->server = strdup(conn->remote_host);
+  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;
-  
-  /* Put it to the ID cache */
-  silc_idcache_add(conn->client_cache, conn->nickname, SILC_ID_CLIENT,
-                  conn->local_id, (void *)conn->local_entry, TRUE);
-}
-
-/* Processed received Channel ID for a channel. This is called when client
-   joins to channel and server replies with channel ID. The ID is cached. */
-
-void silc_client_new_channel_id(SilcClient client,
-                               SilcSocketConnection sock,
-                               char *channel_name,
-                               unsigned int mode, SilcIDPayload idp)
-{
-  SilcClientConnection conn = (SilcClientConnection)sock->user_data;
-  SilcChannelEntry channel;
-
-  SILC_LOG_DEBUG(("New channel ID"));
-
-  channel = silc_calloc(1, sizeof(*channel));
-  channel->channel_name = channel_name;
-  channel->id = silc_id_payload_get_id(idp);
-  channel->mode = mode;
-  silc_list_init(channel->clients, struct SilcChannelUserStruct, next);
-
-  conn->current_channel = channel;
+  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);
 
   /* Put it to the ID cache */
-  silc_idcache_add(conn->channel_cache, channel_name, SILC_ID_CHANNEL,
-                  (void *)channel->id, (void *)channel, TRUE);
+  silc_idcache_add(conn->internal->client_cache,
+                  strdup(conn->nickname), conn->local_id,
+                  (void *)conn->local_entry, 0, NULL);
+
+  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 && strcmp(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);
+    }
+  }
 }
 
-/* Saves channel key from encoded `key_payload'. This is used when we
-   receive Channel Key Payload and when we are processing JOIN command 
-   reply. */
+/* Removes a client entry from all channels it has joined. */
 
-void silc_client_save_channel_key(SilcClientConnection conn,
-                                 SilcBuffer key_payload, 
-                                 SilcChannelEntry channel)
+void silc_client_remove_from_channels(SilcClient client,
+                                     SilcClientConnection conn,
+                                     SilcClientEntry client_entry)
 {
-  unsigned char *id_string, *key, *cipher;
-  unsigned int tmp_len;
-  SilcChannelID *id;
-  SilcIDCacheEntry id_cache = NULL;
-  SilcChannelKeyPayload payload;
-
-  payload = silc_channel_key_payload_parse(key_payload);
-  if (!payload)
-    return;
-
-  id_string = silc_channel_key_get_id(payload, &tmp_len);
-  if (!id_string) {
-    silc_channel_key_payload_free(payload);
-    return;
-  }
-
-  id = silc_id_str2id(id_string, SILC_ID_CHANNEL);
-
-  /* Find channel. */
-  if (!channel) {
-    if (!silc_idcache_find_by_id_one(conn->channel_cache, (void *)id,
-                                    SILC_ID_CHANNEL, &id_cache))
-      goto out;
-    
-    /* Get channel entry */
-    channel = (SilcChannelEntry)id_cache->context;
-  }
+  SilcHashTableList htl;
+  SilcChannelUser chu;
 
-  /* Save the key */
-  key = silc_channel_key_get_key(payload, &tmp_len);
-  cipher = silc_channel_key_get_cipher(payload, NULL);
-  channel->key_len = tmp_len;
-  channel->key = silc_calloc(tmp_len, sizeof(*channel->key));
-  memcpy(channel->key, key, tmp_len);
-
-  if (!silc_cipher_alloc(cipher, &channel->channel_key)) {
-    conn->client->ops->say(conn->client, conn,
-                    "Cannot talk to channel: unsupported cipher %s", cipher);
-    goto out;
+  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);
   }
-  channel->channel_key->cipher->set_key(channel->channel_key->context, 
-                                       key, tmp_len);
 
-  /* Client is now joined to the channel */
-  channel->on_channel = TRUE;
-
- out:
-  silc_free(id);
-  silc_channel_key_payload_free(payload);
+  silc_hash_table_list_reset(&htl);
 }
 
-/* Processes received key for channel. The received key will be used
-   to protect the traffic on the channel for now on. Client must receive
-   the key to the channel before talking on the channel is possible. 
-   This is the key that server has generated, this is not the channel
-   private key, it is entirely local setting. */
+/* 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_receive_channel_key(SilcClient client,
-                                    SilcSocketConnection sock,
-                                    SilcBuffer packet)
+void silc_client_replace_from_channels(SilcClient client,
+                                      SilcClientConnection conn,
+                                      SilcClientEntry old,
+                                      SilcClientEntry new)
 {
-  SILC_LOG_DEBUG(("Received key for channel"));
-
-  /* Save the key */
-  silc_client_save_channel_key(sock->user_data, packet, NULL);
-}
+  SilcHashTableList htl;
+  SilcChannelUser chu;
 
-/* Process received message to a channel (or from a channel, really). This
-   decrypts the channel message with channel specific key and parses the
-   channel payload. Finally it displays the message on the screen. */
+  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);
 
-void silc_client_channel_message(SilcClient client, 
-                                SilcSocketConnection sock, 
-                                SilcPacketContext *packet)
-{
-  SilcClientConnection conn = (SilcClientConnection)sock->user_data;
-  SilcBuffer buffer = packet->buffer;
-  SilcChannelPayload payload = NULL;
-  SilcChannelID *id = NULL;
-  SilcChannelEntry channel;
-  SilcChannelUser chu;
-  SilcIDCacheEntry id_cache = NULL;
-  SilcClientID *client_id = NULL;
-  char *nickname;
-
-  /* Sanity checks */
-  if (packet->dst_id_type != SILC_ID_CHANNEL)
-    goto out;
-
-  client_id = silc_id_str2id(packet->src_id, SILC_ID_CLIENT);
-  id = silc_id_str2id(packet->dst_id, SILC_ID_CHANNEL);
-
-  /* Find the channel entry from channels on this connection */
-  if (!silc_idcache_find_by_id_one(conn->channel_cache, (void *)id,
-                                  SILC_ID_CHANNEL, &id_cache))
-    goto out;
-
-  channel = (SilcChannelEntry)id_cache->context;
-
-  /* Decrypt the channel message payload. Push the IV out of the way,
-     since it is not encrypted (after pushing buffer->tail has the IV). */
-  silc_buffer_push_tail(buffer, 16);
-  channel->channel_key->cipher->decrypt(channel->channel_key->context,
-                                       buffer->data, buffer->data,
-                                       buffer->len, buffer->tail);
-  silc_buffer_pull_tail(buffer, 16);
-
-  /* Parse the channel message payload */
-  payload = silc_channel_payload_parse(buffer);
-  if (!payload)
-    goto out;
-
-  /* Find nickname */
-  nickname = "[unknown]";
-  silc_list_start(channel->clients);
-  while ((chu = silc_list_get(channel->clients)) != SILC_LIST_END) {
-    if (!SILC_ID_CLIENT_COMPARE(chu->client->id, client_id)) {
-      nickname = chu->client->nickname;
-      break;
-    }
+    chu->client = new;
+    silc_hash_table_add(chu->channel->user_list, chu->client, chu);
+    silc_hash_table_add(chu->client->channels, chu->channel, chu);
   }
-
-  /* Pass the message to application */
-  client->ops->channel_message(client, conn, nickname,
-                              channel->channel_name,
-                              silc_channel_get_data(payload, NULL));
-
- out:
-  if (id)
-    silc_free(id);
-  if (client_id)
-    silc_free(client_id);
-  if (payload)
-    silc_channel_payload_free(payload);
+  silc_hash_table_list_reset(&htl);
 }
 
-/* Private message received. This processes the private message and
-   finally displays it on the screen. */
+/* Registers failure timeout to process the received failure packet
+   with timeout. */
 
-void silc_client_private_message(SilcClient client, 
-                                SilcSocketConnection sock, 
+void silc_client_process_failure(SilcClient client,
+                                SilcSocketConnection sock,
                                 SilcPacketContext *packet)
 {
-  SilcClientConnection conn = (SilcClientConnection)sock->user_data;
-  SilcBuffer buffer = packet->buffer;
-  unsigned short nick_len;
-  unsigned char *nickname, *message;
+  SilcUInt32 failure = 0;
 
-  /* Get nickname */
-  silc_buffer_unformat(buffer, 
-                      SILC_STR_UI16_NSTRING_ALLOC(&nickname, &nick_len),
-                      SILC_STR_END);
-  silc_buffer_pull(buffer, 2 + nick_len);
-     
-  message = silc_calloc(buffer->len + 1, sizeof(char));
-  memcpy(message, buffer->data, buffer->len);
-
-  /* Pass the private message to application */
-  client->ops->private_message(client, conn, nickname, message);
-
-  /* See if we are away (gone). If we are away we will reply to the
-     sender with the set away message. */
-  if (conn->away && conn->away->away) {
-    SilcClientID *remote_id;
-    SilcClientEntry remote_client;
-    SilcIDCacheEntry id_cache;
-
-    if (packet->src_id_type != SILC_ID_CLIENT)
-      goto out;
-
-    remote_id = silc_id_str2id(packet->src_id, SILC_ID_CLIENT);
-    if (!remote_id)
-      goto out;
-
-    /* If it's me, ignore */
-    if (!SILC_ID_CLIENT_COMPARE(remote_id, conn->local_id))
-      goto out;
-
-    /* Check whether we know this client already */
-    if (!silc_idcache_find_by_id_one(conn->client_cache, remote_id,
-                                    SILC_ID_CLIENT, &id_cache))
-      {
-       /* Allocate client entry */
-       remote_client = silc_calloc(1, sizeof(*remote_client));
-       remote_client->id = remote_id;
-       silc_parse_nickname(nickname, &remote_client->nickname, 
-                           &remote_client->server, &remote_client->num);
-
-       /* Save the client to cache */
-       silc_idcache_add(conn->client_cache, remote_client->nickname,
-                        SILC_ID_CLIENT, remote_client->id, remote_client, 
-                        TRUE);
-      } else {
-       silc_free(remote_id);
-       remote_client = (SilcClientEntry)id_cache->context;
-      }
+  if (sock->protocol) {
+    if (packet->buffer->len >= 4)
+      SILC_GET32_MSB(failure, packet->buffer->data);
 
-    /* Send the away message */
-    silc_client_packet_send_private_message(client, sock, remote_client,
-                                           conn->away->away,
-                                           strlen(conn->away->away), TRUE);
+    /* Notify application */
+    client->internal->ops->failure(client, sock->user_data, sock->protocol,
+                                  (void *)failure);
   }
-
- out:
-  memset(message, 0, buffer->len);
-  silc_free(message);
-  silc_free(nickname);
 }
 
-/* Removes a client entry from all channel it has joined. This really is
-   a performance killer (client_entry should have pointers to channel 
-   entry list). */
+/* A timeout callback for the re-key. We will be the initiator of the
+   re-key protocol. */
 
-void silc_client_remove_from_channels(SilcClient client,
-                                     SilcClientConnection conn,
-                                     SilcClientEntry client_entry)
+SILC_TASK_CALLBACK_GLOBAL(silc_client_rekey_callback)
 {
-  SilcIDCacheEntry id_cache;
-  SilcIDCacheList list;
-  SilcChannelEntry channel;
-  SilcChannelUser chu;
+  SilcSocketConnection sock = (SilcSocketConnection)context;
+  SilcClientConnection conn = (SilcClientConnection)sock->user_data;
+  SilcClient client = (SilcClient)conn->internal->rekey->context;
+  SilcProtocol protocol;
+  SilcClientRekeyInternalContext *proto_ctx;
 
-  if (!silc_idcache_find_by_id(conn->channel_cache, SILC_ID_CACHE_ANY,
-                              SILC_ID_CHANNEL, &list))
+  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;
 
-  silc_idcache_list_first(list, &id_cache);
-  channel = (SilcChannelEntry)id_cache->context;
-  
-  while (channel) {
-    
-    /* Remove client from channel */
-    silc_list_start(channel->clients);
-    while ((chu = silc_list_get(channel->clients)) != SILC_LIST_END) {
-      if (chu->client == client_entry) {
-       silc_list_del(channel->clients, chu);
-       silc_free(chu);
-       break;
-      }
-    }
+  /* 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;
 
-    if (!silc_idcache_list_next(list, &id_cache))
-      break;
-    
-    channel = (SilcChannelEntry)id_cache->context;
-  }
+  /* 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;
 
-  silc_idcache_list_free(list);
+  /* Run the protocol */
+  silc_protocol_execute(protocol, client->schedule, 0, 0);
 }
 
-/* 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. */
+/* The final callback for the REKEY protocol. This will actually take the
+   new key material into use. */
 
-void silc_client_replace_from_channels(SilcClient client, 
-                                      SilcClientConnection conn,
-                                      SilcClientEntry old,
-                                      SilcClientEntry new)
+SILC_TASK_CALLBACK(silc_client_rekey_final)
 {
-  SilcIDCacheEntry id_cache;
-  SilcIDCacheList list;
-  SilcChannelEntry channel;
-  SilcChannelUser chu;
+  SilcProtocol protocol = (SilcProtocol)context;
+  SilcClientRekeyInternalContext *ctx =
+    (SilcClientRekeyInternalContext *)protocol->context;
+  SilcClient client = (SilcClient)ctx->client;
+  SilcSocketConnection sock = ctx->sock;
+  SilcClientConnection conn = (SilcClientConnection)sock->user_data;
+
+  SILC_LOG_DEBUG(("Start"));
 
-  if (!silc_idcache_find_by_id(conn->channel_cache, SILC_ID_CACHE_ANY,
-                              SILC_ID_CHANNEL, &list))
+  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;
+  }
 
-  silc_idcache_list_first(list, &id_cache);
-  channel = (SilcChannelEntry)id_cache->context;
-  
-  while (channel) {
-    
-    /* Replace client entry */
-    silc_list_start(channel->clients);
-    while ((chu = silc_list_get(channel->clients)) != SILC_LIST_END) {
-      if (chu->client == old) {
-       chu->client = new;
-       break;
-      }
-    }
+  /* 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);
 
-    if (!silc_idcache_list_next(list, &id_cache))
-      break;
-    
-    channel = (SilcChannelEntry)id_cache->context;
-  }
+  /* 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);
 
-  silc_idcache_list_free(list);
+  /* 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);
 }
 
-/* Parses mode mask and returns the mode as string. */
+/* 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. */
 
-char *silc_client_chmode(unsigned int mode)
+void silc_client_connection_auth_request(SilcClient client,
+                                        SilcSocketConnection sock,
+                                        SilcPacketContext *packet)
 {
-  char string[20];
-
-  if (!mode)
-    return NULL;
-
-  memset(string, 0, sizeof(string));
-
-  if (mode & SILC_CHANNEL_MODE_PRIVATE)
-    strncat(string, "p", 1);
-
-  if (mode & SILC_CHANNEL_MODE_SECRET)
-    strncat(string, "s", 1);
-
-  if (mode & SILC_CHANNEL_MODE_PRIVKEY)
-    strncat(string, "k", 1);
-
-  if (mode & SILC_CHANNEL_MODE_INVITE)
-    strncat(string, "i", 1);
+  SilcClientConnection conn = (SilcClientConnection)sock->user_data;
+  SilcUInt16 conn_type, auth_meth;
+  int ret;
 
-  if (mode & SILC_CHANNEL_MODE_TOPIC)
-    strncat(string, "t", 1);
+  /* If we haven't send our request then ignore this one. */
+  if (!conn->internal->connauth)
+    return;
 
-  if (mode & SILC_CHANNEL_MODE_ULIMIT)
-    strncat(string, "l", 1);
+  /* 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;
 
-  if (mode & SILC_CHANNEL_MODE_PASSPHRASE)
-    strncat(string, "a", 1);
+  /* 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);
 
-  /* Rest of mode is ignored */
+  silc_schedule_task_del(client->schedule, conn->internal->connauth->timeout);
 
-  return strdup(string);
+  silc_free(conn->internal->connauth);
+  conn->internal->connauth = NULL;
 }
 
-/* Parses channel user mode mask and returns te mode as string */
+/* Timeout task callback called if the server does not reply to our
+   connection authentication method request in the specified time interval. */
 
-char *silc_client_chumode(unsigned int mode)
+SILC_TASK_CALLBACK(silc_client_request_authentication_method_timeout)
 {
-  char string[4];
-
-  if (!mode)
-    return NULL;
+  SilcClientConnection conn = (SilcClientConnection)context;
+  SilcClient client = conn->client;
 
-  memset(string, 0, sizeof(string));
-
-  if (mode & SILC_CHANNEL_UMODE_CHANFO)
-    strncat(string, "f", 1);
+  if (!conn->internal->connauth)
+    return;
 
-  if (mode & SILC_CHANNEL_UMODE_CHANOP)
-    strncat(string, "o", 1);
+  /* 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);
 
-  return strdup(string);
+  silc_free(conn->internal->connauth);
+  conn->internal->connauth = NULL;
 }
 
-/* Parses channel user mode and returns it as special mode character. */
-
-char *silc_client_chumode_char(unsigned int mode)
+/* 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)
 {
-  char string[4];
+  SilcClientConnAuthRequest connauth;
+  SilcBuffer packet;
 
-  if (!mode)
-    return NULL;
+  assert(client && conn);
+  connauth = silc_calloc(1, sizeof(*connauth));
+  connauth->callback = callback;
+  connauth->context = context;
 
-  memset(string, 0, sizeof(string));
+  if (conn->internal->connauth)
+    silc_free(conn->internal->connauth);
 
-  if (mode & SILC_CHANNEL_UMODE_CHANFO)
-    strncat(string, "*", 1);
+  conn->internal->connauth = connauth;
 
-  if (mode & SILC_CHANNEL_UMODE_CHANOP)
-    strncat(string, "@", 1);
+  /* 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);
 
-  return strdup(string);
+  /* 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);
 }