+ list = silc_dlist_init();
+ if (!list)
+ return NULL;
+
+ silc_dlist_start(channel->internal.private_keys);
+ while ((entry = silc_dlist_get(channel->internal.private_keys)))
+ silc_dlist_add(list, entry);
+
+ return list;
+}
+
+/* Sets the `key' to be used as current channel private key on the
+ `channel'. Packet sent after calling this function will be secured
+ with `key'. */
+
+void silc_client_current_channel_private_key(SilcClient client,
+ SilcClientConnection conn,
+ SilcChannelEntry channel,
+ SilcChannelPrivateKey key)
+{
+ if (!channel)
+ return;
+ channel->internal.curr_key = key;
+ channel->cipher = silc_cipher_get_name(key->send_key);
+ channel->hmac = silc_hmac_get_name(key->hmac);
+}
+
+/***************************** Utility routines *****************************/
+
+/* Returns the SilcChannelUser entry if the `client_entry' is joined on the
+ channel indicated by the `channel'. NULL if client is not joined on
+ the channel. */
+
+SilcChannelUser silc_client_on_channel(SilcChannelEntry channel,
+ SilcClientEntry client_entry)
+{
+ SilcChannelUser chu;
+
+ if (silc_hash_table_find(channel->user_list, client_entry, NULL,
+ (void *)&chu))
+ return chu;
+
+ return NULL;
+}
+
+/* Adds client to channel. Returns TRUE if user was added or is already
+ added to the channel, FALSE on error. Must be called with both `channel'
+ and `client_entry' locked. */
+
+SilcBool silc_client_add_to_channel(SilcClient client,
+ SilcClientConnection conn,
+ SilcChannelEntry channel,
+ SilcClientEntry client_entry,
+ SilcUInt32 cumode)
+{
+ SilcChannelUser chu;
+
+ 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;
+
+ chu->client = client_entry;
+ chu->channel = channel;
+ chu->mode = cumode;
+
+ silc_client_ref_client(client, conn, client_entry);
+ silc_client_ref_channel(client, conn, channel);
+
+ silc_hash_table_add(channel->user_list, client_entry, chu);
+ silc_hash_table_add(client_entry->channels, channel, chu);
+
+ return TRUE;
+}
+
+/* Removes client from a channel. Returns FALSE if user is not on channel.
+ This handles entry locking internally. */
+
+SilcBool silc_client_remove_from_channel(SilcClient client,
+ SilcClientConnection conn,
+ SilcChannelEntry channel,
+ SilcClientEntry client_entry)
+{
+ SilcChannelUser chu;
+
+ chu = silc_client_on_channel(channel, client_entry);
+ if (!chu)
+ return FALSE;
+
+ SILC_LOG_DEBUG(("Remove client %s from channel", client_entry->nickname));
+
+ silc_rwlock_wrlock(client_entry->internal.lock);
+ silc_rwlock_wrlock(channel->internal.lock);
+
+ 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_rwlock_unlock(client_entry->internal.lock);
+ silc_rwlock_unlock(channel->internal.lock);
+
+ silc_client_unref_client(client, conn, client_entry);
+ silc_client_unref_channel(client, conn, channel);
+
+ return TRUE;
+}
+
+/* Removes a client entry from all channels it has joined. This handles
+ entry locking internally. */
+
+void silc_client_remove_from_channels(SilcClient client,
+ SilcClientConnection conn,
+ SilcClientEntry client_entry)
+{
+ SilcHashTableList htl;
+ SilcChannelUser chu;
+
+ if (!silc_hash_table_count(client_entry->channels))
+ return;
+
+ SILC_LOG_DEBUG(("Remove client from all joined channels"));
+
+ silc_rwlock_wrlock(client_entry->internal.lock);
+
+ silc_hash_table_list(client_entry->channels, &htl);
+ while (silc_hash_table_get(&htl, NULL, (void *)&chu)) {
+ silc_rwlock_wrlock(chu->channel->internal.lock);
+
+ 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_rwlock_unlock(chu->channel->internal.lock);
+
+ silc_client_unref_client(client, conn, chu->client);
+ silc_client_unref_channel(client, conn, chu->channel);
+ silc_free(chu);
+ }
+
+ silc_rwlock_unlock(client_entry->internal.lock);
+
+ silc_hash_table_list_reset(&htl);
+}
+
+/* Empties channel from users. This handles entry locking internally. */
+
+void silc_client_empty_channel(SilcClient client,
+ SilcClientConnection conn,
+ SilcChannelEntry channel)
+{
+ SilcHashTableList htl;
+ SilcChannelUser chu;
+
+ silc_rwlock_wrlock(channel->internal.lock);
+
+ silc_hash_table_list(channel->user_list, &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_client_unref_client(client, conn, chu->client);
+ silc_client_unref_channel(client, conn, chu->channel);
+ silc_free(chu);