+/************************* Channel Entry Routines ***************************/
+
+/* Add new channel entry to the ID Cache */
+
+SilcChannelEntry silc_client_add_channel(SilcClient client,
+ SilcClientConnection conn,
+ const char *channel_name,
+ SilcUInt32 mode,
+ SilcChannelID *channel_id)
+{
+ SilcChannelEntry channel;
+ char *channel_namec, name[256 + 1];
+
+ SILC_LOG_DEBUG(("Adding channel %s", channel_name));
+
+ channel = silc_calloc(1, sizeof(*channel));
+ if (!channel)
+ return NULL;
+
+ silc_rwlock_alloc(&channel->internal.lock);
+ silc_atomic_init32(&channel->internal.refcnt, 0);
+ silc_atomic_init32(&channel->internal.deleted, 1);
+ channel->id = *channel_id;
+ channel->mode = mode;
+
+ silc_parse_userfqdn(channel_name, name, sizeof(name),
+ channel->server, sizeof(channel->server));
+ if (client->internal->params->full_channel_names)
+ channel->channel_name = strdup(channel_name);
+ else
+ channel->channel_name = strdup(name);
+
+ if (!channel->channel_name) {
+ silc_rwlock_free(channel->internal.lock);
+ silc_atomic_uninit32(&channel->internal.refcnt);
+ silc_free(channel);
+ return NULL;
+ }
+
+ channel->user_list = silc_hash_table_alloc(1, silc_hash_ptr, NULL, NULL,
+ NULL, NULL, NULL, TRUE);
+ if (!channel->user_list) {
+ silc_rwlock_free(channel->internal.lock);
+ silc_atomic_uninit32(&channel->internal.refcnt);
+ silc_free(channel->channel_name);
+ silc_free(channel);
+ return NULL;
+ }
+
+ /* Normalize channel name */
+ channel_namec = silc_channel_name_check(name, strlen(name),
+ SILC_STRING_UTF8, 256, NULL);
+ if (!channel_namec) {
+ silc_rwlock_free(channel->internal.lock);
+ silc_atomic_uninit32(&channel->internal.refcnt);
+ silc_free(channel->channel_name);
+ silc_hash_table_free(channel->user_list);
+ silc_free(channel);
+ return NULL;
+ }
+
+ silc_mutex_lock(conn->internal->lock);
+
+ /* Add channel to cache, the normalized channel name is saved to cache */
+ if (!silc_idcache_add(conn->internal->channel_cache, channel_namec,
+ &channel->id, channel)) {
+ silc_rwlock_free(channel->internal.lock);
+ silc_atomic_uninit32(&channel->internal.refcnt);
+ silc_free(channel_namec);
+ silc_free(channel->channel_name);
+ silc_hash_table_free(channel->user_list);
+ silc_free(channel);
+ silc_mutex_unlock(conn->internal->lock);
+ return NULL;
+ }
+
+ silc_mutex_unlock(conn->internal->lock);
+ silc_client_ref_channel(client, conn, channel);
+
+ SILC_LOG_DEBUG(("Added %p", channel));
+
+ return channel;
+}
+
+/* Removes channel from the cache by the channel entry. */
+
+SilcBool silc_client_del_channel(SilcClient client, SilcClientConnection conn,
+ SilcChannelEntry channel)
+{
+ if (!channel)
+ return FALSE;
+
+ SILC_LOG_DEBUG(("Marking channel entry %p deleted"));
+
+ if (silc_atomic_sub_int32(&channel->internal.deleted, 1) != 0) {
+ SILC_LOG_DEBUG(("Channel entry %p already marked deleted"));
+ return FALSE;
+ }
+
+ silc_client_unref_channel(client, conn, channel);
+ return TRUE;
+}
+
+/* Replaces the channel ID of the `channel' to `new_id'. Returns FALSE
+ if the ID could not be changed. This handles entry locking internally. */
+
+SilcBool silc_client_replace_channel_id(SilcClient client,
+ SilcClientConnection conn,
+ SilcChannelEntry channel,
+ SilcChannelID *new_id)
+{
+ SilcBool ret = FALSE;
+
+ if (!new_id)
+ return FALSE;
+
+ SILC_LOG_DEBUG(("Old Channel ID id(%s)",
+ silc_id_render(&channel->id, SILC_ID_CHANNEL)));
+ SILC_LOG_DEBUG(("New Channel ID id(%s)",
+ silc_id_render(new_id, SILC_ID_CHANNEL)));
+
+ /* Update the ID */
+ silc_rwlock_wrlock(channel->internal.lock);
+ silc_mutex_lock(conn->internal->lock);
+ silc_idcache_update_by_context(conn->internal->channel_cache, channel,
+ new_id, NULL, FALSE);
+ silc_mutex_unlock(conn->internal->lock);
+ silc_rwlock_unlock(channel->internal.lock);
+
+ return ret;
+}
+
+/* Lock channel */
+
+void silc_client_lock_channel(SilcChannelEntry channel_entry)
+{
+ silc_rwlock_rdlock(channel_entry->internal.lock);
+}
+
+/* Unlock channel */
+
+void silc_client_unlock_channel(SilcChannelEntry channel_entry)
+{
+ silc_rwlock_unlock(channel_entry->internal.lock);
+}
+
+/* Take reference of channel entry */
+
+SilcChannelEntry silc_client_ref_channel(SilcClient client,
+ SilcClientConnection conn,
+ SilcChannelEntry channel_entry)
+{
+ silc_atomic_add_int32(&channel_entry->internal.refcnt, 1);
+ SILC_LOG_DEBUG(("Channel %p refcnt %d->%d", channel_entry,
+ silc_atomic_get_int32(&channel_entry->internal.refcnt) - 1,
+ silc_atomic_get_int32(&channel_entry->internal.refcnt)));
+ return channel_entry;
+}
+
+/* Release reference of channel entry */
+
+void silc_client_unref_channel(SilcClient client, SilcClientConnection conn,
+ SilcChannelEntry channel_entry)