+/* 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);
+ }
+
+ silc_rwlock_unlock(channel->internal.lock);
+
+ silc_hash_table_list_reset(&htl);
+}
+
+/* Save public keys to channel public key list. Removes keys that are
+ marked to be removed. Must be called with `channel' locked. */