Initial client library rewrite, connects to remote server already.
[silc.git] / lib / silcclient / client_prvmsg.c
index 3b43a1f808de058873225f0f786b62d88165696b..6c6c56ca22709c1ed5cf710779b075f8c9062f99 100644 (file)
@@ -4,7 +4,7 @@
 
   Author: Pekka Riikonen <priikone@silcnet.org>
 
-  Copyright (C) 1997 - 2004 Pekka Riikonen
+  Copyright (C) 1997 - 2006 Pekka Riikonen
 
   This program is free software; you can redistribute it and/or modify
   it under the terms of the GNU General Public License as published by
 
 */
 /* $Id$ */
-/* This file includes the private message sending and receiving routines
-   and private message key handling routines. */
 
 #include "silc.h"
 #include "silcclient.h"
 #include "client_internal.h"
 
-/* Sends private message to remote client. If private message key has
-   not been set with this client then the message will be encrypted using
-   normal session keys. Private messages are special packets in SILC
-   network hence we need this own function for them. This is similiar
-   to silc_client_packet_send_to_channel except that we send private
-   message. The `data' is the private message. If the `force_send' is
-   TRUE the packet is sent immediately. */
+/************************** Private Message Send ****************************/
+
+/* Sends private message to remote client. */
 
 SilcBool silc_client_send_private_message(SilcClient client,
-                                     SilcClientConnection conn,
-                                     SilcClientEntry client_entry,
-                                     SilcMessageFlags flags,
-                                     unsigned char *data,
-                                     SilcUInt32 data_len,
-                                     SilcBool force_send)
+                                         SilcClientConnection conn,
+                                         SilcClientEntry client_entry,
+                                         SilcMessageFlags flags,
+                                         unsigned char *data,
+                                         SilcUInt32 data_len)
 {
-  SilcSocketConnection sock;
   SilcBuffer buffer;
-  SilcPacketContext packetdata;
-  const SilcBufferStruct packet;
-  SilcCipher cipher;
-  SilcHmac hmac;
-  int block_len;
-  SilcBool ret = FALSE;
-
-  assert(client && conn && client_entry);
-  sock = conn->sock;
+  SilcBool ret;
+
   SILC_LOG_DEBUG(("Sending private message"));
 
+  if (!client || !conn || !client_entry)
+    return FALSE;
+
   /* Encode private message payload */
-  buffer = silc_message_payload_encode(flags, data, data_len,
-                                      !client_entry->send_key ? FALSE :
-                                      !client_entry->generated,
-                                      TRUE, client_entry->send_key,
-                                      client_entry->hmac_send,
-                                      client->rng, NULL, client->private_key,
-                                      client->sha1hash);
+  buffer =
+    silc_message_payload_encode(flags, data, data_len,
+                               (!client_entry->internal.send_key ? FALSE :
+                                !client_entry->internal.generated),
+                               TRUE, client_entry->internal.send_key,
+                               client_entry->internal.hmac_send,
+                               client->rng, NULL, conn->private_key,
+                               client->sha1hash, NULL);
   if (!buffer) {
     SILC_LOG_ERROR(("Error encoding private message"));
     return FALSE;
   }
 
-  /* If we don't have private message specific key then private messages
-     are just as any normal packet thus call normal packet sending.  If
-     the key exist then the encryption process is a bit different and
-     will be done in the rest of this function. */
-  if (!client_entry->send_key) {
-    silc_client_packet_send(client, sock, SILC_PACKET_PRIVATE_MESSAGE,
-                           client_entry->id, SILC_ID_CLIENT, NULL, NULL,
-                           buffer->data, buffer->len, force_send);
-    ret = TRUE;
-    goto out;
-  }
-
-  /* We have private message specific key */
-
-  /* Get data used in the encryption */
-  cipher = conn->internal->send_key;
-  hmac = conn->internal->hmac_send;
-  block_len = silc_cipher_get_block_len(cipher);
-
-  /* Set the packet context pointers. */
-  data = buffer->data;
-  data_len = buffer->len;
-  packetdata.flags = SILC_PACKET_FLAG_PRIVMSG_KEY;
-  packetdata.type = SILC_PACKET_PRIVATE_MESSAGE;
-  packetdata.src_id = conn->local_id_data;
-  packetdata.src_id_len = silc_id_get_len(conn->local_id, SILC_ID_CLIENT);
-  packetdata.src_id_type = SILC_ID_CLIENT;
-  packetdata.dst_id = silc_id_id2str(client_entry->id, SILC_ID_CLIENT);
-  packetdata.dst_id_len = silc_id_get_len(client_entry->id, SILC_ID_CLIENT);
-  packetdata.dst_id_type = SILC_ID_CLIENT;
-  data_len = SILC_PACKET_DATALEN(data_len, SILC_PACKET_HEADER_LEN +
-                                packetdata.src_id_len +
-                                packetdata.dst_id_len);
-  packetdata.truelen = data_len + SILC_PACKET_HEADER_LEN +
-    packetdata.src_id_len + packetdata.dst_id_len;
-  SILC_PACKET_PADLEN((SILC_PACKET_HEADER_LEN +
-                     packetdata.src_id_len +
-                     packetdata.dst_id_len), block_len, packetdata.padlen);
-
-  /* Create the outgoing packet */
-  if (!silc_packet_assemble(&packetdata, client->rng, cipher, hmac, sock,
-                            data, data_len, (const SilcBuffer)&packet)) {
-    SILC_LOG_ERROR(("Error assembling packet"));
-    goto out;
-  }
+  /* Send the private message packet */
+  ret = silc_packet_send_ext(conn->stream, SILC_PACKET_PRIVATE_MESSAGE,
+                            client_entry->internal.send_key ?
+                            SILC_PACKET_FLAG_PRIVMSG_KEY : 0,
+                            0, NULL, SILC_ID_CLIENT, &client_entry->id,
+                            silc_buffer_datalen(buffer), NULL, NULL);
 
-  /* Encrypt the header and padding of the packet. */
-  silc_packet_encrypt(cipher, hmac, conn->internal->psn_send++,
-                     (SilcBuffer)&packet, SILC_PACKET_HEADER_LEN +
-                     packetdata.src_id_len + packetdata.dst_id_len +
-                     packetdata.padlen);
-
-  SILC_LOG_HEXDUMP(("Private message packet, len %d", packet.len),
-                  packet.data, packet.len);
-
-  /* Now actually send the packet */
-  silc_client_packet_send_real(client, sock, force_send);
-
-  /* Check for mandatory rekey */
-  if (conn->internal->psn_send == SILC_CLIENT_REKEY_THRESHOLD)
-    silc_schedule_task_add(client->schedule, sock->sock,
-                          silc_client_rekey_callback, sock, 0, 1,
-                          SILC_TASK_TIMEOUT, SILC_TASK_PRI_NORMAL);
-
-  silc_free(packetdata.dst_id);
-
-  ret = TRUE;
-
- out:
   silc_buffer_free(buffer);
-
   return ret;
 }
 
-static void silc_client_private_message_cb(SilcClient client,
-                                          SilcClientConnection conn,
-                                          SilcClientEntry *clients,
-                                          SilcUInt32 clients_count,
-                                          void *context)
+/************************* Private Message Receive **************************/
+
+/* Private message waiting context */
+typedef struct {
+  SilcMutex wait_lock;
+  SilcCond wait_cond;
+  SilcDList message_queue;
+  unsigned int stopped      : 1;
+} *SilcClientPrivateMessageWait;
+
+/* Client resolving callback.  This continues the private message packet
+   processing in the packet processor thread, which is in waiting state
+   (for incoming packets) when we get here.  We can safely continue in
+   the thread and then return back to waiting when we do it synchronously. */
+
+static void silc_client_private_message_resolved(SilcClient client,
+                                                SilcClientConnection conn,
+                                                SilcStatus status,
+                                                SilcDList clients,
+                                                void *context)
 {
-  SilcPacketContext *packet = (SilcPacketContext *)context;
-
   if (!clients) {
-    silc_packet_context_free(packet);
+    silc_packet_free(context);
     return;
   }
 
-  silc_client_private_message(client, conn->sock, packet);
-  silc_packet_context_free(packet);
+  /* Continue processing the private message packet */
+  silc_fsm_set_state_context(&conn->internal->packet_thread, context);
+  silc_fsm_next(&conn->internal->packet_thread, silc_client_private_message);
+  silc_fsm_continue_sync(&conn->internal->packet_thread);
 }
 
-/* Private message received. This processes the private message and
-   finally displays it on the screen. */
+/* Private message received. */
 
-void silc_client_private_message(SilcClient client,
-                                SilcSocketConnection sock,
-                                SilcPacketContext *packet)
+SILC_FSM_STATE(silc_client_private_message)
 {
-  SilcClientConnection conn = (SilcClientConnection)sock->user_data;
+  SilcClientConnection conn = fsm_context;
+  SilcClient client = conn->client;
+  SilcPacket packet = state_context;
   SilcMessagePayload payload = NULL;
-  SilcClientID *remote_id = NULL;
+  SilcClientID remote_id;
   SilcClientEntry remote_client;
   SilcMessageFlags flags;
   unsigned char *message;
   SilcUInt32 message_len;
-  SilcCipher cipher = NULL;
-  SilcHmac hmac = NULL;
+  SilcClientPrivateMessageWait pmw;
 
   if (packet->src_id_type != SILC_ID_CLIENT)
     goto out;
 
-  remote_id = silc_id_str2id(packet->src_id, packet->src_id_len,
-                            SILC_ID_CLIENT);
-  if (!remote_id)
+  if (!silc_id_str2id(packet->src_id, packet->src_id_len, SILC_ID_CLIENT,
+                     &remote_id, sizeof(remote_id)))
     goto out;
 
   /* Check whether we know this client already */
-  remote_client = silc_client_get_client_by_id(client, conn, remote_id);
+  remote_client = silc_client_get_client_by_id(client, conn, &remote_id);
   if (!remote_client || !remote_client->nickname) {
-    if (remote_client) {
-      if (remote_client->status & SILC_CLIENT_STATUS_RESOLVING) {
-       remote_client->status &= ~SILC_CLIENT_STATUS_RESOLVING;
-       goto out;
-      }
-      remote_client->status |= SILC_CLIENT_STATUS_RESOLVING;
-      remote_client->resolve_cmd_ident = conn->cmd_ident + 1;
-    }
-
-    /* Resolve the client info */
-    silc_client_get_client_by_id_resolve(client, conn, remote_id, NULL,
-                                        silc_client_private_message_cb,
-                                        silc_packet_context_dup(packet));
-    return;
+    /* Resolve the client info.  We return back to packet thread to receive
+       other packets while we wait for the resolving to finish. */
+    silc_client_get_client_by_id_resolve(client, conn, &remote_id, NULL,
+                                        silc_client_private_message_resolved,
+                                        packet);
+    silc_fsm_next(fsm, silc_client_connection_st_packet);
+    return SILC_FSM_CONTINUE;
   }
 
-  cipher = remote_client->receive_key;
-  hmac = remote_client->hmac_receive;
-  if (packet->flags & SILC_PACKET_FLAG_PRIVMSG_KEY && !cipher && !hmac) {
-    silc_free(remote_id);
-    return;
-  }
+  if (packet->flags & SILC_PACKET_FLAG_PRIVMSG_KEY &&
+      !remote_client->internal.receive_key &&
+      !remote_client->internal.hmac_receive)
+    goto out;
 
   /* Parse the payload and decrypt it also if private message key is set */
-  payload = silc_message_payload_parse(packet->buffer->data,
-                                      packet->buffer->len, TRUE,
-                                      !remote_client->generated,
-                                      cipher, hmac);
-  if (!payload) {
-    silc_free(remote_id);
-    return;
-  }
+  payload =
+    silc_message_payload_parse(silc_buffer_datalen(&packet->buffer),
+                              TRUE, !remote_client->internal.generated,
+                              remote_client->internal.receive_key,
+                              remote_client->internal.hmac_receive,
+                              NULL, FALSE, NULL);
+  if (!payload)
+    goto out;
 
-  flags = silc_message_get_flags(payload);
+#if 0 /* We need to rethink this.  This doesn't work with multiple
+        waiters, and performance is suboptimal. */
+  /* Check if some thread is waiting for this private message */
+  silc_mutex_lock(conn->internal->lock);
+  if (conn->internal->privmsg_wait &&
+      silc_hash_table_find_ext(conn->internal->privmsg_wait,
+                              &remote_client->id, NULL, (void **)&pmw,
+                              NULL, NULL, silc_hash_id_compare_full,
+                              SILC_32_TO_PTR(SILC_ID_CLIENT))) {
+    /* Signal that message was received */
+    silc_mutex_unlock(conn->internal->lock);
+    silc_mutex_lock(pmw->wait_lock);
+    if (!pmw->stopped) {
+      silc_dlist_add(pmw->message_queue, payload);
+      silc_cond_broadcast(pmw->wait_cond);
+      silc_mutex_unlock(pmw->wait_lock);
+      silc_packet_free(packet);
+      goto out;
+    }
+    silc_mutex_unlock(pmw->wait_lock);
+  } else
+    silc_mutex_unlock(conn->internal->lock);
+#endif /* 0 */
 
   /* Pass the private message to application */
+  flags = silc_message_get_flags(payload);
   message = silc_message_get_data(payload, &message_len);
   client->internal->ops->private_message(client, conn, remote_client, payload,
                                         flags, message, message_len);
@@ -230,7 +182,7 @@ void silc_client_private_message(SilcClient client,
   if (conn->internal->away && conn->internal->away->away &&
       !(flags & SILC_MESSAGE_FLAG_NOREPLY)) {
     /* If it's me, ignore */
-    if (SILC_ID_CLIENT_COMPARE(remote_id, conn->local_id))
+    if (SILC_ID_CLIENT_COMPARE(&remote_id, conn->local_id))
       goto out;
 
     /* Send the away message */
@@ -238,75 +190,217 @@ void silc_client_private_message(SilcClient client,
                                     SILC_MESSAGE_FLAG_AUTOREPLY |
                                     SILC_MESSAGE_FLAG_NOREPLY,
                                     conn->internal->away->away,
-                                    strlen(conn->internal->away->away), TRUE);
+                                    strlen(conn->internal->away->away));
   }
 
  out:
+  /** Packet processed */
   if (payload)
     silc_message_payload_free(payload);
-  silc_free(remote_id);
+  silc_packet_free(packet);
+  silc_fsm_next(fsm, silc_client_connection_st_packet);
+  return SILC_FSM_CONTINUE;
+}
+
+#if 0 /* XXX we need to rethink this */
+/* Initialize private message waiting in a thread. */
+
+void *silc_client_private_message_wait_init(SilcClientConnection conn,
+                                           SilcClientEntry client_entry)
+{
+  SilcClientPrivateMessageWait pmw;
+
+  pmw = silc_calloc(1, sizeof(*pmw));
+  if (!pmw)
+    return NULL;
+
+  pmw->message_queue = silc_dlist_init();
+  if (!pmw->message_queue) {
+    silc_free(pmw);
+    return NULL;
+  }
+
+  /* Allocate mutex and conditional variable */
+  if (!silc_mutex_alloc(&pmw->wait_lock)) {
+    silc_dlist_uninit(pmw->message_queue);
+    silc_free(pmw);
+    return NULL;
+  }
+  if (!silc_cond_alloc(&pmw->wait_cond)) {
+    silc_dlist_uninit(pmw->message_queue);
+    silc_mutex_free(pmw->wait_lock);
+    silc_free(pmw);
+    return NULL;
+  }
+
+  silc_mutex_lock(conn->internal->lock);
+
+  /* Allocate waiting hash table */
+  if (!conn->internal->privmsg_wait) {
+    conn->internal->privmsg_wait =
+      silc_hash_table_alloc(0, silc_hash_id,
+                           SILC_32_TO_PTR(SILC_ID_CLIENT),
+                           silc_hash_id_compare,
+                           SILC_32_TO_PTR(SILC_ID_CLIENT), NULL, NULL, TRUE);
+    if (!conn->internal->privmsg_wait) {
+      silc_mutex_unlock(conn->internal->lock);
+      silc_dlist_uninit(pmw->message_queue);
+      silc_mutex_free(pmw->wait_lock);
+      silc_cond_free(pmw->wait_cond);
+      silc_free(pmw);
+      return NULL;
+    }
+  }
+
+  /* Add to waiting hash table */
+  silc_hash_table_add(conn->internal->privmsg_wait, client_entry->id, pmw);
+
+  silc_mutex_unlock(conn->internal->lock);
+
+  return (void *)pmw;
+}
+
+/* Uninitialize private message waiting. */
+
+void silc_client_private_message_wait_uninit(SilcClientConnection conn,
+                                            SilcClientEntry client_entry,
+                                            void *waiter)
+{
+  SilcClientPrivateMessageWait pmw = waiter;
+  SilcMessagePayload payload;
+
+  /* Signal any threads to stop waiting */
+  silc_mutex_lock(pmw->wait_lock);
+  pmw->stopped = TRUE;
+  silc_cond_broadcast(pmw->wait_cond);
+  silc_mutex_unlock(pmw->wait_lock);
+
+  /* Re-acquire lock and free resources */
+  silc_mutex_lock(pmw->wait_lock);
+
+  /* Free any remaining message */
+  silc_dlist_start(pmw->message_queue);
+  while ((payload = silc_dlist_get(pmw->message_queue)))
+    silc_message_payload_free(payload);
+
+  silc_dlist_uninit(pmw->message_queue);
+  silc_cond_free(pmw->wait_cond);
+  silc_mutex_unlock(pmw->wait_lock);
+  silc_mutex_free(pmw->wait_lock);
+
+  silc_mutex_lock(conn->internal->lock);
+  silc_hash_table_del_by_context(conn->internal->privmsg_wait,
+                                client_entry->id, pmw);
+  silc_mutex_unlock(conn->internal->lock);
+
+  silc_free(pmw);
+}
+
+/* Blocks the calling process or thread until a private message has been
+   received from the specified client. */
+
+SilcBool silc_client_private_message_wait(SilcClientConnection conn,
+                                         SilcClientEntry client_entry,
+                                         void *waiter,
+                                         SilcMessagePayload *payload)
+{
+  SilcClientPrivateMessageWait pmw = waiter;
+  SilcPacket packet;
+
+  silc_mutex_lock(pmw->wait_lock);
+
+  /* Wait here until private message has been received */
+  while (silc_dlist_count(pmw->message_queue) == 0) {
+    if (pmw->stopped) {
+      silc_mutex_unlock(pmw->wait_lock);
+      return FALSE;
+    }
+    silc_cond_wait(pmw->wait_cond, pmw->wait_lock);
+  }
+
+  /* Return message */
+  silc_dlist_start(pmw->message_queue);
+  *payload = silc_dlist_get(pmw->message_queue);
+  silc_dlist_del(pmw->message_queue, *payload);
+
+  silc_mutex_unlock(pmw->wait_lock);
+
+  return TRUE;
 }
+#endif /* 0 */
 
-/* Function that actually employes the received private message key */
+/*************************** Private Message Key ****************************/
+
+/* Client resolving callback.  Here we simply mark that we are the responder
+   side of this private message key request.  */
 
 static void silc_client_private_message_key_cb(SilcClient client,
                                               SilcClientConnection conn,
-                                              SilcClientEntry *clients,
-                                              SilcUInt32 clients_count,
+                                              SilcStatus status,
+                                              SilcDList clients,
                                               void *context)
 {
-  SilcPacketContext *packet = (SilcPacketContext *)context;
-  unsigned char *key;
-  SilcUInt16 key_len;
+  SilcPacket packet = context;
   unsigned char *cipher = NULL, *hmac = NULL;
+  SilcClientEntry client_entry;
   int ret;
 
-  if (!clients)
-    goto out;
+  if (!clients) {
+    silc_packet_free(packet);
+    return;
+  }
 
   /* Parse the private message key payload */
-  ret = silc_buffer_unformat(packet->buffer,
-                            SILC_STR_UI16_NSTRING(&key, &key_len),
+  ret = silc_buffer_unformat(&packet->buffer,
                             SILC_STR_UI16_STRING_ALLOC(&cipher),
                             SILC_STR_UI16_STRING_ALLOC(&hmac),
                             SILC_STR_END);
   if (!ret)
     goto out;
 
-  if (key_len > packet->buffer->len)
-    goto out;
-
   /* Mark that we are responder */
-  clients[0]->prv_resp = TRUE;
+  client_entry = silc_dlist_get(clients);
+  client_entry->internal.prv_resp = TRUE;
+
+  /* XXX we should notify application that remote wants to set up the
+     static key */
 
  out:
   silc_free(cipher);
   silc_free(hmac);
-  silc_packet_context_free(packet);
+  silc_packet_free(packet);
 }
 
 /* Processes incoming Private Message Key payload to indicate that the
    sender whishes to set up a static private message key. */
 
-void silc_client_private_message_key(SilcClient client,
-                                    SilcSocketConnection sock,
-                                    SilcPacketContext *packet)
+SILC_FSM_STATE(silc_client_private_message_key)
 {
-  SilcClientID *remote_id;
+  SilcClientConnection conn = fsm_context;
+  SilcClient client = conn->client;
+  SilcPacket packet = state_context;
+  SilcClientID remote_id;
 
-  if (packet->src_id_type != SILC_ID_CLIENT)
-    return;
+  if (packet->src_id_type != SILC_ID_CLIENT) {
+    silc_packet_free(packet);
+    goto out;
+  }
 
-  remote_id = silc_id_str2id(packet->src_id, packet->src_id_len,
-                            SILC_ID_CLIENT);
-  if (!remote_id)
-    return;
+  if (!silc_id_str2id(packet->src_id, packet->src_id_len, SILC_ID_CLIENT,
+                     &remote_id, sizeof(remote_id))) {
+    silc_packet_free(packet);
+    goto out;
+  }
 
-  silc_client_get_client_by_id_resolve(client, sock->user_data, remote_id,
-                                      NULL,
+  /* Always resolve the remote client.  The actual packet is processed
+     in the resolving callback. */
+  silc_client_get_client_by_id_resolve(client, conn, &remote_id, NULL,
                                       silc_client_private_message_key_cb,
-                                      silc_packet_context_dup(packet));
-  silc_free(remote_id);
+                                      packet);
+
+ out:
+  silc_fsm_next(fsm, silc_client_connection_st_packet);
+  return SILC_FSM_CONTINUE;
 }
 
 /* Adds private message key to the client library. The key will be used to
@@ -331,24 +425,26 @@ void silc_client_private_message_key(SilcClient client,
    otherwise. */
 
 SilcBool silc_client_add_private_message_key(SilcClient client,
-                                        SilcClientConnection conn,
-                                        SilcClientEntry client_entry,
-                                        const char *cipher,
-                                        const char *hmac,
-                                        unsigned char *key,
-                                        SilcUInt32 key_len,
-                                        SilcBool generate_key,
-                                        SilcBool responder)
+                                            SilcClientConnection conn,
+                                            SilcClientEntry client_entry,
+                                            const char *cipher,
+                                            const char *hmac,
+                                            unsigned char *key,
+                                            SilcUInt32 key_len,
+                                            SilcBool generate_key,
+                                            SilcBool responder)
 {
   unsigned char private_key[32];
   SilcUInt32 len;
+  SilcSKEKeyMaterial keymat;
+  SilcBool ret;
   int i;
-  SilcSKEKeyMaterial *keymat;
 
-  assert(client && client_entry);
+  if (!client || !client_entry)
+    return FALSE;
 
   /* Return FALSE if key already set */
-  if (client_entry->send_key && client_entry->receive_key)
+  if (client_entry->internal.send_key && client_entry->internal.receive_key)
     return FALSE;
 
   if (!cipher)
@@ -369,55 +465,30 @@ SilcBool silc_client_add_private_message_key(SilcClient client,
       private_key[i] = silc_rng_get_byte_fast(client->rng);
     key = private_key;
     key_len = len;
-    client_entry->generated = TRUE;
   }
 
   /* Save the key */
-  client_entry->key = silc_memdup(key, key_len);
-  client_entry->key_len = key_len;
+  client_entry->internal.key = silc_memdup(key, key_len);
+  client_entry->internal.key_len = key_len;
 
   /* Produce the key material as the protocol defines */
-  keymat = silc_calloc(1, sizeof(*keymat));
-  if (silc_ske_process_key_material_data(key, key_len, 16, 256, 16,
-                                        client->sha1hash, keymat)
-      != SILC_SKE_STATUS_OK)
+  keymat = silc_ske_process_key_material_data(key, key_len, 16, 256, 16,
+                                             client->sha1hash);
+  if (!keymat)
     return FALSE;
 
-  /* Allocate the cipher and HMAC */
-  silc_cipher_alloc(cipher, &client_entry->send_key);
-  silc_cipher_alloc(cipher, &client_entry->receive_key);
-  silc_hmac_alloc(hmac, NULL, &client_entry->hmac_send);
-  silc_hmac_alloc(hmac, NULL, &client_entry->hmac_receive);
+  /* Set the key into use */
+  ret = silc_client_add_private_message_key_ske(client, conn, client_entry,
+                                               cipher, hmac, keymat,
+                                               responder);
 
-  /* Set the keys */
-  if (responder == TRUE) {
-    silc_cipher_set_key(client_entry->send_key, keymat->receive_enc_key,
-                       keymat->enc_key_len);
-    silc_cipher_set_iv(client_entry->send_key, keymat->receive_iv);
-    silc_cipher_set_key(client_entry->receive_key, keymat->send_enc_key,
-                       keymat->enc_key_len);
-    silc_cipher_set_iv(client_entry->receive_key, keymat->send_iv);
-    silc_hmac_set_key(client_entry->hmac_send, keymat->receive_hmac_key,
-                     keymat->hmac_key_len);
-    silc_hmac_set_key(client_entry->hmac_receive, keymat->send_hmac_key,
-                     keymat->hmac_key_len);
-  } else {
-    silc_cipher_set_key(client_entry->send_key, keymat->send_enc_key,
-                       keymat->enc_key_len);
-    silc_cipher_set_iv(client_entry->send_key, keymat->send_iv);
-    silc_cipher_set_key(client_entry->receive_key, keymat->receive_enc_key,
-                       keymat->enc_key_len);
-    silc_cipher_set_iv(client_entry->receive_key, keymat->receive_iv);
-    silc_hmac_set_key(client_entry->hmac_send, keymat->send_hmac_key,
-                     keymat->hmac_key_len);
-    silc_hmac_set_key(client_entry->hmac_receive, keymat->receive_hmac_key,
-                     keymat->hmac_key_len);
-  }
+  if (!generate_key)
+    client_entry->internal.generated = FALSE;
 
   /* Free the key material */
   silc_ske_free_key_material(keymat);
 
-  return TRUE;
+  return ret;
 }
 
 /* Same as above but takes the key material from the SKE key material
@@ -427,17 +498,18 @@ SilcBool silc_client_add_private_message_key(SilcClient client,
    the SKE protocol. */
 
 SilcBool silc_client_add_private_message_key_ske(SilcClient client,
-                                            SilcClientConnection conn,
-                                            SilcClientEntry client_entry,
-                                            const char *cipher,
-                                            const char *hmac,
-                                            SilcSKEKeyMaterial *key,
-                                            SilcBool responder)
+                                                SilcClientConnection conn,
+                                                SilcClientEntry client_entry,
+                                                const char *cipher,
+                                                const char *hmac,
+                                                SilcSKEKeyMaterial keymat,
+                                                SilcBool responder)
 {
-  assert(client && client_entry);
+  if (!client || !client_entry)
+    return FALSE;
 
   /* Return FALSE if key already set */
-  if (client_entry->send_key && client_entry->receive_key)
+  if (client_entry->internal.send_key && client_entry->internal.receive_key)
     return FALSE;
 
   if (!cipher)
@@ -451,37 +523,51 @@ SilcBool silc_client_add_private_message_key_ske(SilcClient client,
   if (!silc_hmac_is_supported(hmac))
     return FALSE;
 
-  client_entry->generated = TRUE;
+  client_entry->internal.generated = TRUE;
 
   /* Allocate the cipher and HMAC */
-  silc_cipher_alloc(cipher, &client_entry->send_key);
-  silc_cipher_alloc(cipher, &client_entry->receive_key);
-  silc_hmac_alloc(hmac, NULL, &client_entry->hmac_send);
-  silc_hmac_alloc(hmac, NULL, &client_entry->hmac_receive);
+  if (!silc_cipher_alloc(cipher, &client_entry->internal.send_key))
+    return FALSE;
+  if (!silc_cipher_alloc(cipher, &client_entry->internal.receive_key))
+    return FALSE;
+  if (!silc_hmac_alloc(hmac, NULL, &client_entry->internal.hmac_send))
+    return FALSE;
+  if (!silc_hmac_alloc(hmac, NULL, &client_entry->internal.hmac_receive))
+    return FALSE;
 
   /* Set the keys */
   if (responder == TRUE) {
-    silc_cipher_set_key(client_entry->send_key, key->receive_enc_key,
-                       key->enc_key_len);
-    silc_cipher_set_iv(client_entry->send_key, key->receive_iv);
-    silc_cipher_set_key(client_entry->receive_key, key->send_enc_key,
-                       key->enc_key_len);
-    silc_cipher_set_iv(client_entry->receive_key, key->send_iv);
-    silc_hmac_set_key(client_entry->hmac_send, key->receive_hmac_key,
-                     key->hmac_key_len);
-    silc_hmac_set_key(client_entry->hmac_receive, key->send_hmac_key,
-                     key->hmac_key_len);
+    silc_cipher_set_key(client_entry->internal.send_key,
+                       keymat->receive_enc_key,
+                       keymat->enc_key_len);
+    silc_cipher_set_iv(client_entry->internal.send_key,
+                      keymat->receive_iv);
+    silc_cipher_set_key(client_entry->internal.receive_key,
+                       keymat->send_enc_key,
+                       keymat->enc_key_len);
+    silc_cipher_set_iv(client_entry->internal.receive_key, keymat->send_iv);
+    silc_hmac_set_key(client_entry->internal.hmac_send,
+                     keymat->receive_hmac_key,
+                     keymat->hmac_key_len);
+    silc_hmac_set_key(client_entry->internal.hmac_receive,
+                     keymat->send_hmac_key,
+                     keymat->hmac_key_len);
   } else {
-    silc_cipher_set_key(client_entry->send_key, key->send_enc_key,
-                       key->enc_key_len);
-    silc_cipher_set_iv(client_entry->send_key, key->send_iv);
-    silc_cipher_set_key(client_entry->receive_key, key->receive_enc_key,
-                       key->enc_key_len);
-    silc_cipher_set_iv(client_entry->receive_key, key->receive_iv);
-    silc_hmac_set_key(client_entry->hmac_send, key->send_hmac_key,
-                     key->hmac_key_len);
-    silc_hmac_set_key(client_entry->hmac_receive, key->receive_hmac_key,
-                     key->hmac_key_len);
+    silc_cipher_set_key(client_entry->internal.send_key,
+                       keymat->send_enc_key,
+                       keymat->enc_key_len);
+    silc_cipher_set_iv(client_entry->internal.send_key,
+                      keymat->send_iv);
+    silc_cipher_set_key(client_entry->internal.receive_key,
+                       keymat->receive_enc_key,
+                       keymat->enc_key_len);
+    silc_cipher_set_iv(client_entry->internal.receive_key, keymat->receive_iv);
+    silc_hmac_set_key(client_entry->internal.hmac_send,
+                     keymat->send_hmac_key,
+                     keymat->hmac_key_len);
+    silc_hmac_set_key(client_entry->internal.hmac_receive,
+                     keymat->receive_hmac_key,
+                     keymat->hmac_key_len);
   }
 
   return TRUE;
@@ -491,46 +577,48 @@ SilcBool silc_client_add_private_message_key_ske(SilcClient client,
    going to be the initiator, if and when, the users set up a static
    private message key (not Key Agreement). */
 
-SilcBool silc_client_send_private_message_key_request(SilcClient client,
-                                                 SilcClientConnection conn,
-                                                 SilcClientEntry client_entry)
+SilcBool
+silc_client_send_private_message_key_request(SilcClient client,
+                                            SilcClientConnection conn,
+                                            SilcClientEntry client_entry)
 {
-  SilcSocketConnection sock;
-  SilcBuffer buffer;
+  SilcBufferStruct buffer;
   int cipher_len, hmac_len;
   const char *cipher, *hmac;
+  SilcBool ret;
 
-  assert(client && conn && client_entry);
+  if (!client || !conn || !client_entry)
+    return FALSE;
 
-  sock = conn->sock;
-  if (!client_entry->send_key || !client_entry->key)
+  if (!client_entry->internal.send_key && !client_entry->internal.receive_key)
     return FALSE;
 
   SILC_LOG_DEBUG(("Sending private message key indicator"));
 
-  cipher = silc_cipher_get_name(client_entry->send_key);
+  cipher = silc_cipher_get_name(client_entry->internal.send_key);
   cipher_len = strlen(cipher);
-  hmac = silc_hmac_get_name(client_entry->hmac_send);
+  hmac = silc_hmac_get_name(client_entry->internal.hmac_send);
   hmac_len = strlen(hmac);
 
   /* Create private message key payload */
-  buffer = silc_buffer_alloc_size(4 + cipher_len + hmac_len);
-  silc_buffer_format(buffer,
-                    SILC_STR_UI_SHORT(cipher_len),
-                    SILC_STR_UI_XNSTRING(cipher,
-                                         cipher_len),
-                    SILC_STR_UI_SHORT(hmac_len),
-                    SILC_STR_UI_XNSTRING(hmac,
-                                         hmac_len),
-                    SILC_STR_END);
+  memset(&buffer, 0, sizeof(buffer));
+  if (silc_buffer_format(&buffer,
+                        SILC_STR_UI_SHORT(cipher_len),
+                        SILC_STR_UI_XNSTRING(cipher,
+                                             cipher_len),
+                        SILC_STR_UI_SHORT(hmac_len),
+                        SILC_STR_UI_XNSTRING(hmac,
+                                             hmac_len),
+                        SILC_STR_END) < 0)
+    return FALSE;
 
   /* Send the packet */
-  silc_client_packet_send(client, sock, SILC_PACKET_PRIVATE_MESSAGE_KEY,
-                         client_entry->id, SILC_ID_CLIENT, NULL, NULL,
-                         buffer->data, buffer->len, TRUE);
-  silc_free(buffer);
+  ret = silc_packet_send_ext(conn->stream, SILC_PACKET_PRIVATE_MESSAGE_KEY,
+                            0, 0, NULL, SILC_ID_CLIENT, &client_entry->id,
+                            silc_buffer_datalen(&buffer), NULL, NULL);
+  silc_buffer_purge(&buffer);
 
-  return TRUE;
+  return ret;
 }
 
 /* Removes the private message from the library. The key won't be used
@@ -538,25 +626,26 @@ SilcBool silc_client_send_private_message_key_request(SilcClient client,
    client. Returns FALSE on error, TRUE otherwise. */
 
 SilcBool silc_client_del_private_message_key(SilcClient client,
-                                       SilcClientConnection conn,
-                                       SilcClientEntry client_entry)
+                                            SilcClientConnection conn,
+                                            SilcClientEntry client_entry)
 {
-  assert(client && client_entry);
+  if (!client || !client_entry)
+    return FALSE;
 
-  if (!client_entry->send_key && !client_entry->receive_key)
+  if (!client_entry->internal.send_key && !client_entry->internal.receive_key)
     return FALSE;
 
-  silc_cipher_free(client_entry->send_key);
-  silc_cipher_free(client_entry->receive_key);
+  silc_cipher_free(client_entry->internal.send_key);
+  silc_cipher_free(client_entry->internal.receive_key);
 
-  if (client_entry->key) {
-    memset(client_entry->key, 0, client_entry->key_len);
-    silc_free(client_entry->key);
+  if (client_entry->internal.key) {
+    memset(client_entry->internal.key, 0, client_entry->internal.key_len);
+    silc_free(client_entry->internal.key);
   }
 
-  client_entry->send_key = NULL;
-  client_entry->receive_key = NULL;
-  client_entry->key = NULL;
+  client_entry->internal.send_key = NULL;
+  client_entry->internal.receive_key = NULL;
+  client_entry->internal.key = NULL;
 
   return TRUE;
 }
@@ -576,36 +665,33 @@ silc_client_list_private_message_keys(SilcClient client,
 {
   SilcPrivateMessageKeys keys;
   SilcUInt32 count = 0;
+  SilcList list;
   SilcIDCacheEntry id_cache;
-  SilcIDCacheList list;
   SilcClientEntry entry;
 
-  assert(client && conn);
+  if (!client || !conn)
+    return NULL;
 
   if (!silc_idcache_get_all(conn->internal->client_cache, &list))
     return NULL;
 
-  if (!silc_idcache_list_count(list)) {
-    silc_idcache_list_free(list);
+  keys = silc_calloc(silc_list_count(list), sizeof(*keys));
+  if (!keys)
     return NULL;
-  }
-
-  keys = silc_calloc(silc_idcache_list_count(list), sizeof(*keys));
 
-  silc_idcache_list_first(list, &id_cache);
-  while (id_cache) {
-    entry = (SilcClientEntry)id_cache->context;
-
-    if (entry->send_key) {
+  silc_list_start(list);
+  while ((id_cache = silc_list_get(list))) {
+    entry = id_cache->context;
+    if (entry->internal.send_key) {
       keys[count].client_entry = entry;
-      keys[count].cipher = (char *)silc_cipher_get_name(entry->send_key);
-      keys[count].key = entry->generated == FALSE ? entry->key : NULL;
-      keys[count].key_len = entry->generated == FALSE ? entry->key_len : 0;
+      keys[count].cipher = (char *)silc_cipher_get_name(entry->internal.
+                                                       send_key);
+      keys[count].key = (entry->internal.generated == FALSE ?
+                        entry->internal.key : NULL);
+      keys[count].key_len = (entry->internal.generated == FALSE ?
+                            entry->internal.key_len : 0);
       count++;
     }
-
-    if (!silc_idcache_list_next(list, &id_cache))
-      break;
   }
 
   if (key_count)