Fixed channel key setting.
[silc.git] / lib / silcclient / client_channel.c
index 34d51c64881a98940bf474ed3f466030c7396e09..4492153c0320b9d264edafb04d543c0bbbe6c09a 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
@@ -94,12 +94,12 @@ 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;
   }
 
@@ -163,10 +163,13 @@ SILC_FSM_STATE(silc_client_channel_message)
 
   SILC_LOG_DEBUG(("Received channel message"));
 
+  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;
+    SILC_FSM_CONTINUE;
   }
 
   if (silc_unlikely(!silc_id_str2id(packet->src_id,
@@ -174,7 +177,7 @@ SILC_FSM_STATE(silc_client_channel_message)
                                    &remote_id, sizeof(remote_id)))) {
     /** Invalid source ID */
     silc_fsm_next(fsm, silc_client_channel_message_error);
-    return SILC_FSM_CONTINUE;
+    SILC_FSM_CONTINUE;
   }
 
   /* Get sender client entry */
@@ -194,7 +197,7 @@ SILC_FSM_STATE(silc_client_channel_message)
                                    sizeof(channel_id)))) {
     /** Invalid destination ID */
     silc_fsm_next(fsm, silc_client_channel_message_error);
-    return SILC_FSM_CONTINUE;
+    SILC_FSM_CONTINUE;
   }
 
   /* Find the channel */
@@ -202,7 +205,7 @@ SILC_FSM_STATE(silc_client_channel_message)
   if (silc_unlikely(!channel)) {
     /** Unknown channel */
     silc_fsm_next(fsm, silc_client_channel_message_error);
-    return SILC_FSM_CONTINUE;
+    SILC_FSM_CONTINUE;
   }
 
   /* Check that user is on channel */
@@ -211,7 +214,7 @@ SILC_FSM_STATE(silc_client_channel_message)
     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;
+    SILC_FSM_CONTINUE;
   }
 
   /* If there is no channel private key then just decrypt the message
@@ -221,7 +224,7 @@ 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);
 
@@ -262,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);
 
@@ -295,7 +298,7 @@ SILC_FSM_STATE(silc_client_channel_message)
   silc_client_unref_channel(client, conn, channel);
   if (payload)
     silc_message_payload_free(payload);
-  return SILC_FSM_FINISH;
+  SILC_FSM_FINISH;
 }
 
 /* Channel message error. */
@@ -304,7 +307,7 @@ SILC_FSM_STATE(silc_client_channel_message_error)
 {
   SilcPacket packet = state_context;
   silc_packet_free(packet);
-  return SILC_FSM_FINISH;
+  SILC_FSM_FINISH;
 }
 
 /******************************* Channel Key ********************************/
@@ -390,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,
@@ -399,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,
@@ -410,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, TRUE);
+  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 ?
@@ -455,7 +469,7 @@ SILC_FSM_STATE(silc_client_channel_key)
   silc_client_save_channel_key(client, conn, &packet->buffer, NULL);
   silc_packet_free(packet);
 
-  return SILC_FSM_FINISH;
+  SILC_FSM_FINISH;
 }
 
 /**************************** Channel Private Key ***************************/
@@ -701,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;
@@ -731,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);
 
@@ -750,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);