SILC_FSM_* macro API changes.
[silc.git] / lib / silcclient / client_channel.c
index bb5c071e4c49eb69e8affdc51c5b8dc2e41fc4ab..4d320bfbe10abc364b8c4e685185cc65250acfe0 100644 (file)
@@ -4,7 +4,7 @@
 
   Author: Pekka Riikonen <priikone@silcnet.org>
 
-  Copyright (C) 1997 - 2004, 2006 Pekka Riikonen
+  Copyright (C) 1997 - 2007 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
@@ -43,15 +43,15 @@ SilcBool silc_client_send_channel_message(SilcClient client,
 
   SILC_LOG_DEBUG(("Sending channel message"));
 
-  if (!client || !conn || !channel)
+  if (silc_unlikely(!client || !conn || !channel))
     return FALSE;
-  if (flags & SILC_MESSAGE_FLAG_SIGNED && !hash)
+  if (silc_unlikely(flags & SILC_MESSAGE_FLAG_SIGNED && !hash))
     return FALSE;
-  if (conn->internal->disconnected)
+  if (silc_unlikely(conn->internal->disconnected))
     return FALSE;
 
   chu = silc_client_on_channel(channel, conn->local_entry);
-  if (!chu) {
+  if (silc_unlikely(!chu)) {
     client->internal->ops->say(conn->client, conn,
                               SILC_CLIENT_MESSAGE_AUDIT,
                               "Cannot talk to channel: not joined");
@@ -59,13 +59,14 @@ SilcBool silc_client_send_channel_message(SilcClient client,
   }
 
   /* Check if it is allowed to send messages to this channel by us. */
-  if (channel->mode & SILC_CHANNEL_MODE_SILENCE_USERS && !chu->mode)
+  if (silc_unlikely(channel->mode & SILC_CHANNEL_MODE_SILENCE_USERS &&
+                   !chu->mode))
     return FALSE;
-  if (channel->mode & SILC_CHANNEL_MODE_SILENCE_OPERS &&
-      chu->mode & SILC_CHANNEL_UMODE_CHANOP &&
-      !(chu->mode & SILC_CHANNEL_UMODE_CHANFO))
+  if (silc_unlikely(channel->mode & SILC_CHANNEL_MODE_SILENCE_OPERS &&
+                   chu->mode & SILC_CHANNEL_UMODE_CHANOP &&
+                   !(chu->mode & SILC_CHANNEL_UMODE_CHANFO)))
     return FALSE;
-  if (chu->mode & SILC_CHANNEL_UMODE_QUIET)
+  if (silc_unlikely(chu->mode & SILC_CHANNEL_UMODE_QUIET))
     return FALSE;
 
   /* Take the key to be used */
@@ -93,16 +94,16 @@ SilcBool silc_client_send_channel_message(SilcClient client,
       channel->internal.curr_key = key;
     } else {
       /* Use normal channel key generated by the server */
-      cipher = channel->internal.channel_key;
+      cipher = channel->internal.send_key;
       hmac = channel->internal.hmac;
     }
   } else {
     /* Use normal channel key generated by the server */
-    cipher = channel->internal.channel_key;
+    cipher = channel->internal.send_key;
     hmac = channel->internal.hmac;
   }
 
-  if (!cipher || !hmac) {
+  if (silc_unlikely(!cipher || !hmac)) {
     SILC_LOG_ERROR(("No cipher and HMAC for channel"));
     return FALSE;
   }
@@ -111,7 +112,7 @@ SilcBool silc_client_send_channel_message(SilcClient client,
   buffer = silc_message_payload_encode(flags, data, data_len, TRUE, FALSE,
                                       cipher, hmac, client->rng, NULL,
                                       conn->private_key, hash, NULL);
-  if (!buffer) {
+  if (silc_unlikely(!buffer)) {
     SILC_LOG_ERROR(("Error encoding channel message"));
     return FALSE;
   }
@@ -162,14 +163,18 @@ SILC_FSM_STATE(silc_client_channel_message)
 
   SILC_LOG_DEBUG(("Received channel message"));
 
-  if (packet->dst_id_type != SILC_ID_CHANNEL) {
+  SILC_LOG_HEXDUMP(("Channel message"), silc_buffer_data(buffer),
+                  silc_buffer_len(buffer));
+
+  if (silc_unlikely(packet->dst_id_type != SILC_ID_CHANNEL)) {
     /** Invalid packet */
     silc_fsm_next(fsm, silc_client_channel_message_error);
     return SILC_FSM_CONTINUE;
   }
 
-  if (!silc_id_str2id(packet->src_id, packet->src_id_len, SILC_ID_CLIENT,
-                     &remote_id, sizeof(remote_id))) {
+  if (silc_unlikely(!silc_id_str2id(packet->src_id,
+                                   packet->src_id_len, SILC_ID_CLIENT,
+                                   &remote_id, sizeof(remote_id)))) {
     /** Invalid source ID */
     silc_fsm_next(fsm, silc_client_channel_message_error);
     return SILC_FSM_CONTINUE;
@@ -187,8 +192,9 @@ SILC_FSM_STATE(silc_client_channel_message)
     /* NOT REACHED */
   }
 
-  if (!silc_id_str2id(packet->dst_id, packet->dst_id_len, SILC_ID_CHANNEL,
-                     &channel_id, sizeof(channel_id))) {
+  if (silc_unlikely(!silc_id_str2id(packet->dst_id, packet->dst_id_len,
+                                   SILC_ID_CHANNEL, &channel_id,
+                                   sizeof(channel_id)))) {
     /** Invalid destination ID */
     silc_fsm_next(fsm, silc_client_channel_message_error);
     return SILC_FSM_CONTINUE;
@@ -196,12 +202,21 @@ SILC_FSM_STATE(silc_client_channel_message)
 
   /* Find the channel */
   channel = silc_client_get_channel_by_id(client, conn, &channel_id);
-  if (!channel) {
+  if (silc_unlikely(!channel)) {
     /** Unknown channel */
     silc_fsm_next(fsm, silc_client_channel_message_error);
     return SILC_FSM_CONTINUE;
   }
 
+  /* Check that user is on channel */
+  if (silc_unlikely(!silc_client_on_channel(channel, client_entry))) {
+    /** User not on channel */
+    SILC_LOG_WARNING(("Message from user not on channel, client or "
+                     "server bug"));
+    silc_fsm_next(fsm, silc_client_channel_message_error);
+    return SILC_FSM_CONTINUE;
+  }
+
   /* If there is no channel private key then just decrypt the message
      with the channel key. If private keys are set then just go through
      all private keys and check what decrypts correctly. */
@@ -209,14 +224,14 @@ SILC_FSM_STATE(silc_client_channel_message)
     /* Parse the channel message payload. This also decrypts the payload */
     payload = silc_message_payload_parse(silc_buffer_data(buffer),
                                         silc_buffer_len(buffer), FALSE,
-                                        FALSE, channel->internal.channel_key,
+                                        FALSE, channel->internal.receive_key,
                                         channel->internal.hmac, NULL,
                                         FALSE, NULL);
 
     /* If decryption failed and we have just performed channel key rekey
        we will use the old key in decryption. If that fails too then we
        cannot do more and will drop the packet. */
-    if (!payload) {
+    if (silc_unlikely(!payload)) {
       SilcCipher cipher;
       SilcHmac hmac;
 
@@ -250,7 +265,7 @@ SILC_FSM_STATE(silc_client_channel_message)
       payload = silc_message_payload_parse(silc_buffer_data(buffer),
                                           silc_buffer_len(buffer),
                                           FALSE, FALSE,
-                                          channel->internal.channel_key,
+                                          channel->internal.receive_key,
                                           channel->internal.hmac, NULL,
                                           FALSE, NULL);
 
@@ -339,6 +354,8 @@ SilcBool silc_client_save_channel_key(SilcClient client,
   SilcChannelID id;
   SilcChannelKeyPayload payload;
 
+  SILC_LOG_DEBUG(("New channel key"));
+
   payload = silc_channel_key_payload_parse(silc_buffer_data(key_payload),
                                           silc_buffer_len(key_payload));
   if (!payload)
@@ -359,6 +376,7 @@ SilcBool silc_client_save_channel_key(SilcClient client,
   if (!channel) {
     channel = silc_client_get_channel_by_id(client, conn, &id);
     if (!channel) {
+      SILC_LOG_DEBUG(("Key for unknown channel"));
       silc_channel_key_payload_free(payload);
       return FALSE;
     }
@@ -375,7 +393,7 @@ SilcBool silc_client_save_channel_key(SilcClient client,
     channel->internal.old_hmacs = silc_dlist_init();
   if (channel->internal.old_channel_keys && channel->internal.old_hmacs) {
     silc_dlist_add(channel->internal.old_channel_keys,
-                  channel->internal.channel_key);
+                  channel->internal.receive_key);
     silc_dlist_add(channel->internal.old_hmacs, channel->internal.hmac);
     silc_schedule_task_add_timeout(client->schedule,
                                   silc_client_save_channel_key_rekey,
@@ -384,7 +402,17 @@ SilcBool silc_client_save_channel_key(SilcClient client,
 
   /* Get channel cipher */
   cipher = silc_channel_key_get_cipher(payload, NULL);
-  if (!silc_cipher_alloc(cipher, &channel->internal.channel_key)) {
+  if (!silc_cipher_alloc(cipher, &channel->internal.send_key)) {
+    client->internal->ops->say(
+                          conn->client, conn,
+                          SILC_CLIENT_MESSAGE_AUDIT,
+                          "Cannot talk to channel: unsupported cipher %s",
+                          cipher);
+    silc_client_unref_channel(client, conn, channel);
+    silc_channel_key_payload_free(payload);
+    return FALSE;
+  }
+  if (!silc_cipher_alloc(cipher, &channel->internal.receive_key)) {
     client->internal->ops->say(
                           conn->client, conn,
                           SILC_CLIENT_MESSAGE_AUDIT,
@@ -395,9 +423,10 @@ SilcBool silc_client_save_channel_key(SilcClient client,
     return FALSE;
   }
 
-  /* Set the cipher key */
+  /* Set the cipher key.  Both sending and receiving keys are same */
   key = silc_channel_key_get_key(payload, &tmp_len);
-  silc_cipher_set_key(channel->internal.channel_key, key, tmp_len * 8);
+  silc_cipher_set_key(channel->internal.send_key, key, tmp_len * 8, TRUE);
+  silc_cipher_set_key(channel->internal.receive_key, key, tmp_len * 8, FALSE);
 
   /* Get channel HMAC */
   hmac = (channel->internal.hmac ?
@@ -494,7 +523,7 @@ SilcBool silc_client_add_channel_private_key(SilcClient client,
   }
   entry->name = name ? strdup(name) : NULL;
 
-  /* Allocate the cipher and set the key*/
+  /* Allocate the cipher and set the key */
   if (!silc_cipher_alloc(cipher, &entry->cipher)) {
     silc_free(entry);
     silc_free(entry->name);
@@ -502,7 +531,7 @@ SilcBool silc_client_add_channel_private_key(SilcClient client,
     return FALSE;
   }
   silc_cipher_set_key(entry->cipher, keymat->send_enc_key,
-                     keymat->enc_key_len);
+                     keymat->enc_key_len, TRUE);
 
   /* Generate HMAC key from the channel key data and set it */
   if (!silc_hmac_alloc(hmac, NULL, &entry->hmac)) {
@@ -686,6 +715,8 @@ SilcBool silc_client_add_to_channel(SilcClient client,
   if (silc_client_on_channel(channel, client_entry))
     return TRUE;
 
+  SILC_LOG_DEBUG(("Add client %s to channel", client_entry->nickname));
+
   chu = silc_calloc(1, sizeof(*chu));
   if (!chu)
     return FALSE;
@@ -716,10 +747,16 @@ SilcBool silc_client_remove_from_channel(SilcClient client,
   if (!chu)
     return FALSE;
 
+  SILC_LOG_DEBUG(("Remove client %s from channel", client_entry->nickname));
+
   silc_hash_table_del(chu->client->channels, chu->channel);
   silc_hash_table_del(chu->channel->user_list, chu->client);
   silc_free(chu);
 
+  /* If channel became empty, delete it */
+  if (!silc_hash_table_count(channel->user_list))
+    silc_client_del_channel(client, conn, channel);
+
   silc_client_unref_client(client, conn, client_entry);
   silc_client_unref_channel(client, conn, channel);
 
@@ -735,10 +772,20 @@ void silc_client_remove_from_channels(SilcClient client,
   SilcHashTableList htl;
   SilcChannelUser chu;
 
+  if (!silc_hash_table_count(client_entry->channels))
+    return;
+
+  SILC_LOG_DEBUG(("Remove client from all joined channels"));
+
   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);
+
+    /* If channel became empty, delete it */
+    if (!silc_hash_table_count(chu->channel->user_list))
+      silc_client_del_channel(client, conn, chu->channel);
+
     silc_client_unref_client(client, conn, chu->client);
     silc_client_unref_channel(client, conn, chu->channel);
     silc_free(chu);