+ SILC_LOG_DEBUG(("Resolve channel by id %s",
+ silc_id_render(channel_id, SILC_ID_CHANNEL)));
+
+ i = silc_calloc(1, sizeof(*i));
+ if (!i)
+ return 0;
+ i->completion = completion;
+ i->context = context;
+ i->channels = silc_dlist_init();
+ if (!i->channels) {
+ silc_free(i);
+ return 0;
+ }
+
+ /* Send the command */
+ idp = silc_id_payload_encode(channel_id, SILC_ID_CHANNEL);
+ cmd_ident = silc_client_command_send(client, conn, SILC_COMMAND_IDENTIFY,
+ silc_client_get_channel_cb, i, 1,
+ 5, silc_buffer_datalen(idp));
+ silc_buffer_free(idp);
+ if (!cmd_ident && completion)
+ completion(client, conn, SILC_STATUS_ERR_RESOURCE_LIMIT, NULL, context);
+
+ return cmd_ident;
+}
+
+/************************* 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_init16(&channel->internal.refcnt, 0);
+ 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_uninit16(&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_uninit16(&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_uninit16(&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_uninit16(&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)
+{
+ SilcIDCacheEntry id_cache;
+ SilcBool ret = TRUE;
+ SilcCipher key;
+ SilcHmac hmac;
+ char *namec;
+
+ if (!channel)
+ return FALSE;
+
+ if (silc_atomic_sub_int16(&channel->internal.refcnt, 1) > 0)
+ return FALSE;
+
+ SILC_LOG_DEBUG(("Deleting channel %p", channel));
+
+ silc_mutex_lock(conn->internal->lock);
+ if (silc_idcache_find_by_context(conn->internal->channel_cache, channel,
+ &id_cache)) {
+ namec = id_cache->name;
+ ret = silc_idcache_del_by_context(conn->internal->channel_cache,
+ channel, NULL);
+ silc_free(namec);
+ }
+ silc_mutex_unlock(conn->internal->lock);
+
+ if (!ret)
+ return FALSE;
+
+ silc_client_empty_channel(client, conn, channel);
+ silc_client_del_channel_private_keys(client, conn, channel);
+ silc_hash_table_free(channel->user_list);
+ silc_free(channel->channel_name);
+ silc_free(channel->topic);
+ if (channel->founder_key)
+ silc_pkcs_public_key_free(channel->founder_key);
+ if (channel->internal.send_key)
+ silc_cipher_free(channel->internal.send_key);
+ if (channel->internal.receive_key)
+ silc_cipher_free(channel->internal.receive_key);
+ if (channel->internal.hmac)
+ silc_hmac_free(channel->internal.hmac);
+ if (channel->internal.old_channel_keys) {
+ silc_dlist_start(channel->internal.old_channel_keys);
+ while ((key = silc_dlist_get(channel->internal.old_channel_keys)))
+ silc_cipher_free(key);
+ silc_dlist_uninit(channel->internal.old_channel_keys);
+ }
+ if (channel->internal.old_hmacs) {
+ silc_dlist_start(channel->internal.old_hmacs);
+ while ((hmac = silc_dlist_get(channel->internal.old_hmacs)))
+ silc_hmac_free(hmac);
+ silc_dlist_uninit(channel->internal.old_hmacs);
+ }
+ if (channel->channel_pubkeys)
+ silc_argument_list_free(channel->channel_pubkeys,
+ SILC_ARGUMENT_PUBLIC_KEY);
+ silc_atomic_uninit16(&channel->internal.refcnt);
+ silc_rwlock_free(channel->internal.lock);
+ silc_schedule_task_del_by_context(conn->client->schedule, channel);
+ silc_free(channel);
+
+ return ret;
+}
+
+/* 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);