updates.
[silc.git] / lib / silcclient / client_prvmsg.c
index 0234f0232c3fbb11ca02c3837ff9f87f3052bb89..3da26c854a1ebc00f453a6a62fbd189d2cd647a2 100644 (file)
 void silc_client_send_private_message(SilcClient client,
                                      SilcClientConnection conn,
                                      SilcClientEntry client_entry,
+                                     SilcMessageFlags flags,
                                      unsigned char *data, 
-                                     unsigned int data_len, 
+                                     uint32 data_len, 
                                      int force_send)
 {
   SilcSocketConnection sock = conn->sock;
   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);
+  /* Encode private message payload */
+  buffer = silc_private_message_payload_encode(flags,
+                                              data_len, data,
+                                              client_entry->send_key);
 
   /* If we don't have private message specific key then private messages
      are just as any normal packet thus call normal packet sending.  If
@@ -74,16 +68,16 @@ void silc_client_send_private_message(SilcClient client,
 
   /* Get data used in the encryption */
   cipher = client_entry->send_key;
-  hmac = conn->hmac;
+  hmac = conn->hmac_send;
 
   /* Set the packet context pointers. */
   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_CLIENT_LEN;
+  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_CLIENT_LEN;
+  packetdata.dst_id_len = silc_id_get_len(client_entry->id, SILC_ID_CLIENT);
   packetdata.dst_id_type = SILC_ID_CLIENT;
   packetdata.truelen = buffer->len + SILC_PACKET_HEADER_LEN + 
     packetdata.src_id_len + packetdata.dst_id_len;
@@ -101,12 +95,7 @@ void silc_client_send_private_message(SilcClient client,
   
   packetdata.buffer = sock->outbuf;
 
-  /* Encrypt payload of the packet. Encrypt with private message specific
-     key */
-  cipher->cipher->encrypt(cipher->context, buffer->data, buffer->data,
-                         buffer->len, cipher->iv);
-      
-  /* Put the actual encrypted payload data into the buffer. */
+  /* Put the actual encrypted message payload data into the buffer. */
   silc_buffer_put(sock->outbuf, buffer->data, buffer->len);
 
   /* Create the outgoing packet */
@@ -126,9 +115,26 @@ void silc_client_send_private_message(SilcClient client,
   silc_free(packetdata.dst_id);
 
  out:
-  silc_free(buffer);
+  silc_buffer_free(buffer);
 }     
 
+static void silc_client_private_message_cb(SilcClient client,
+                                          SilcClientConnection conn,
+                                          SilcClientEntry *clients,
+                                          uint32 clients_count,
+                                          void *context)
+{
+  SilcPacketContext *packet = (SilcPacketContext *)context;
+
+  if (!clients) {
+    silc_packet_context_free(packet);
+    return;
+  }
+
+  silc_client_private_message(client, conn->sock, packet);
+  silc_packet_context_free(packet);
+}
+
 /* Private message received. This processes the private message and
    finally displays it on the screen. */
 
@@ -137,77 +143,69 @@ void silc_client_private_message(SilcClient client,
                                 SilcPacketContext *packet)
 {
   SilcClientConnection conn = (SilcClientConnection)sock->user_data;
-  SilcBuffer buffer = packet->buffer;
+  SilcPrivateMessagePayload payload = NULL;
   SilcIDCacheEntry id_cache;
   SilcClientID *remote_id = NULL;
   SilcClientEntry remote_client;
-  unsigned short nick_len;
-  unsigned char *nickname, *message = NULL;
-  int ret;
+  SilcMessageFlags flags;
 
   if (packet->src_id_type != SILC_ID_CLIENT)
     goto out;
 
-  /* Get nickname */
-  ret = silc_buffer_unformat(buffer, 
-                            SILC_STR_UI16_NSTRING_ALLOC(&nickname, &nick_len),
-                            SILC_STR_END);
-  if (ret == -1)
-    return;
-
-  silc_buffer_pull(buffer, 2 + nick_len);
-
-  message = silc_calloc(buffer->len + 1, sizeof(char));
-  memcpy(message, buffer->data, buffer->len);
-
   remote_id = silc_id_str2id(packet->src_id, packet->src_id_len, 
                             SILC_ID_CLIENT);
   if (!remote_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 = silc_id_dup(remote_id, SILC_ID_CLIENT);
-      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,
-                      strlen(remote_client->nickname), SILC_ID_CLIENT, 
-                      remote_client->id, remote_client, TRUE, TRUE);
-    } else {
-      remote_client = (SilcClientEntry)id_cache->context;
-    }
+  if (!silc_idcache_find_by_id_one_ext(conn->client_cache, (void *)remote_id, 
+                                      NULL, NULL, 
+                                      silc_hash_client_id_compare, NULL,
+                                      &id_cache)) {
+    /* Resolve the client info */
+    silc_client_get_client_by_id_resolve(client, conn, remote_id,
+                                        silc_client_private_message_cb,
+                                        silc_packet_context_dup(packet));
+    return;
+  }
+
+  remote_client = (SilcClientEntry)id_cache->context;
+
+  /* Parse the payload and decrypt it also if private message key is set */
+  payload = silc_private_message_payload_parse(packet->buffer,
+                                              remote_client->receive_key);
+  if (!payload) {
+    silc_free(remote_id);
+    return;
+  }
+
+  flags = silc_private_message_get_flags(payload);
 
   /* Pass the private message to application */
-  client->ops->private_message(client, conn, remote_client, message);
+  client->ops->private_message(client, conn, remote_client, flags,
+                              silc_private_message_get_message(payload, 
+                                                               NULL));
 
   /* See if we are away (gone). If we are away we will reply to the
      sender with the set away message. */
-  if (conn->away && conn->away->away) {
+  if (conn->away && conn->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 */
     silc_client_send_private_message(client, conn, remote_client,
+                                    SILC_MESSAGE_FLAG_AUTOREPLY |
+                                    SILC_MESSAGE_FLAG_NOREPLY,
                                     conn->away->away,
                                     strlen(conn->away->away), TRUE);
   }
 
  out:
+  if (payload)
+    silc_private_message_payload_free(payload);
   if (remote_id)
     silc_free(remote_id);
-
-  if (message) {
-    memset(message, 0, buffer->len);
-    silc_free(message);
-  }
-  silc_free(nickname);
 }
 
 /* Function that actually employes the received private message key */
@@ -215,12 +213,12 @@ void silc_client_private_message(SilcClient client,
 static void silc_client_private_message_key_cb(SilcClient client,
                                               SilcClientConnection conn,
                                               SilcClientEntry *clients,
-                                              unsigned int clients_count,
+                                              uint32 clients_count,
                                               void *context)
 {
   SilcPacketContext *packet = (SilcPacketContext *)context;
   unsigned char *key;
-  unsigned short key_len;
+  uint16 key_len;
   unsigned char *cipher;
   int ret;
 
@@ -240,11 +238,11 @@ static void silc_client_private_message_key_cb(SilcClient client,
 
   /* Now take the key in use */
   if (!silc_client_add_private_message_key(client, conn, clients[0],
-                                          cipher, key, key_len, FALSE))
+                                          cipher, key, key_len, FALSE, TRUE))
     goto out;
 
   /* Print some info for application */
-  client->ops->say(client, conn, 
+  client->ops->say(client, conn, SILC_CLIENT_MESSAGE_AUDIT, 
                   "Received private message key from %s%s%s %s%s%s", 
                   clients[0]->nickname,
                   clients[0]->server ? "@" : "",
@@ -289,6 +287,11 @@ void silc_client_private_message_key(SilcClient client,
    requirements of the SILC protocol are met. The API, however, allows
    to allocate any cipher.
 
+   If `responder' is TRUE then the sending and receiving keys will be
+   set according the client being the receiver of the private key.  If
+   FALSE the client is being the sender (or negotiator) of the private
+   key.
+
    It is not necessary to set key for normal private message usage. If the
    key is not set then the private messages are encrypted using normal
    session keys. Setting the private key, however, increases the security. 
@@ -301,11 +304,12 @@ int silc_client_add_private_message_key(SilcClient client,
                                        SilcClientEntry client_entry,
                                        char *cipher,
                                        unsigned char *key,
-                                       unsigned int key_len,
-                                       int generate_key)
+                                       uint32 key_len,
+                                       bool generate_key,
+                                       bool responder)
 {
   unsigned char private_key[32];
-  unsigned int len;
+  uint32 len;
   int i;
   SilcSKEKeyMaterial *keymat;
 
@@ -316,7 +320,7 @@ int silc_client_add_private_message_key(SilcClient client,
     return FALSE;
 
   if (!cipher)
-    cipher = "aes-256-cbc";
+    cipher = SILC_DEFAULT_CIPHER;
 
   /* Check the requested cipher */
   if (!silc_cipher_is_supported(cipher))
@@ -348,12 +352,21 @@ int silc_client_add_private_message_key(SilcClient client,
   silc_cipher_alloc(cipher, &client_entry->receive_key);
 
   /* Set the keys */
-  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);
+  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);
+  } 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);
+  }
 
   /* Free the key material */
   silc_ske_free_key_material(keymat);
@@ -371,7 +384,8 @@ int silc_client_add_private_message_key_ske(SilcClient client,
                                            SilcClientConnection conn,
                                            SilcClientEntry client_entry,
                                            char *cipher,
-                                           SilcSKEKeyMaterial *key)
+                                           SilcSKEKeyMaterial *key,
+                                           bool responder)
 {
   assert(client_entry);
 
@@ -380,7 +394,7 @@ int silc_client_add_private_message_key_ske(SilcClient client,
     return FALSE;
 
   if (!cipher)
-    cipher = "aes-256-cbc";
+    cipher = SILC_DEFAULT_CIPHER;
 
   /* Check the requested cipher */
   if (!silc_cipher_is_supported(cipher))
@@ -391,12 +405,21 @@ int silc_client_add_private_message_key_ske(SilcClient client,
   silc_cipher_alloc(cipher, &client_entry->receive_key);
 
   /* Set the keys */
-  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);
+  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);
+  } 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);
+  }
 
   return TRUE;
 }
@@ -487,16 +510,15 @@ int silc_client_del_private_message_key(SilcClient client,
 SilcPrivateMessageKeys
 silc_client_list_private_message_keys(SilcClient client,
                                      SilcClientConnection conn,
-                                     unsigned int *key_count)
+                                     uint32 *key_count)
 {
   SilcPrivateMessageKeys keys;
-  unsigned int count = 0;
+  uint32 count = 0;
   SilcIDCacheEntry id_cache;
   SilcIDCacheList list;
   SilcClientEntry entry;
 
-  if (!silc_idcache_find_by_id(conn->client_cache, SILC_ID_CACHE_ANY, 
-                              SILC_ID_CLIENT, &list))
+  if (!silc_idcache_get_all(conn->client_cache, &list))
     return NULL;
 
   if (!silc_idcache_list_count(list)) {
@@ -532,7 +554,34 @@ silc_client_list_private_message_keys(SilcClient client,
    silc_client_list_private_message_keys. */
 
 void silc_client_free_private_message_keys(SilcPrivateMessageKeys keys,
-                                          unsigned int key_count)
+                                          uint32 key_count)
 {
   silc_free(keys);
 }
+
+/* Sets away `message'.  The away message may be set when the client's
+   mode is changed to SILC_UMODE_GONE and the client whishes to reply
+   to anyone who sends private message.  The `message' will be sent
+   automatically back to the the client who send private message.  If
+   away message is already set this replaces the old message with the
+   new one.  If `message' is NULL the old away message is removed. 
+   The sender may freely free the memory of the `message'. */
+
+void silc_client_set_away_message(SilcClient client,
+                                 SilcClientConnection conn,
+                                 char *message)
+{
+  if (!message && conn->away) {
+    silc_free(conn->away->away);
+    silc_free(conn->away);
+    conn->away = NULL;
+  }
+
+  if (message) {
+    if (!conn->away)
+      conn->away = silc_calloc(1, sizeof(*conn->away));
+    if (conn->away->away)
+      silc_free(conn->away->away);
+    conn->away->away = strdup(message);
+  }
+}