X-Git-Url: http://git.silcnet.org/gitweb/?p=silc.git;a=blobdiff_plain;f=apps%2Fsilcd%2Fidlist.c;h=9acdd06844330fd6b2e7f93baaa55d344aa68687;hp=358bef33f467826ace5549b419c4c4dc7652fcf5;hb=382d15d447b7a95390decfa783836ae4fe255b3d;hpb=bb1973faaa81ead23b3d5e05b45cddd8a47d51f7 diff --git a/apps/silcd/idlist.c b/apps/silcd/idlist.c index 358bef33..9acdd068 100644 --- a/apps/silcd/idlist.c +++ b/apps/silcd/idlist.c @@ -2,9 +2,9 @@ idlist.c - Author: Pekka Riikonen + Author: Pekka Riikonen - Copyright (C) 1997 - 2000 Pekka Riikonen + Copyright (C) 1997 - 2002 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 @@ -21,6 +21,85 @@ #include "serverincludes.h" #include "idlist.h" +#include "server_internal.h" + +/****************************************************************************** + + Common functions + +******************************************************************************/ + +/* This function is used to add keys and stuff to common ID entry data + structure. */ + +void silc_idlist_add_data(void *entry, SilcIDListData idata) +{ + SilcIDListData data = (SilcIDListData)entry; + data->send_key = idata->send_key; + data->receive_key = idata->receive_key; + data->hmac_send = idata->hmac_send; + data->hmac_receive = idata->hmac_receive; + data->psn_send = idata->psn_send; + data->psn_receive = idata->psn_receive; + data->hash = idata->hash; + data->public_key = idata->public_key; + memcpy(data->fingerprint, idata->fingerprint, sizeof(data->fingerprint)); + data->rekey = idata->rekey; + data->last_receive = idata->last_receive; + data->last_sent = idata->last_sent; + data->status = idata->status; + + data->created = time(0); /* Update creation time */ +} + +/* Free's all data in the common ID entry data structure. */ + +void silc_idlist_del_data(void *entry) +{ + SilcIDListData idata = (SilcIDListData)entry; + if (idata->send_key) + silc_cipher_free(idata->send_key); + if (idata->receive_key) + silc_cipher_free(idata->receive_key); + if (idata->rekey) { + if (idata->rekey->send_enc_key) { + memset(idata->rekey->send_enc_key, 0, idata->rekey->enc_key_len); + silc_free(idata->rekey->send_enc_key); + } + silc_free(idata->rekey); + } + if (idata->hmac_send) + silc_hmac_free(idata->hmac_send); + if (idata->hmac_receive) + silc_hmac_free(idata->hmac_receive); + if (idata->hash) + silc_hash_free(idata->hash); + if (idata->public_key) + silc_pkcs_public_key_free(idata->public_key); + + idata->send_key = NULL; + idata->receive_key = NULL; + idata->rekey = NULL; + idata->hmac_send = NULL; + idata->hmac_receive = NULL; + idata->hash = NULL; + idata->public_key = NULL; +} + +/* Purges ID cache */ + +SILC_TASK_CALLBACK_GLOBAL(silc_idlist_purge) +{ + SilcServer server = app_context; + SilcIDListPurge i = (SilcIDListPurge)context; + + SILC_LOG_DEBUG(("Purging cache")); + + silc_idcache_purge(i->cache); + silc_schedule_task_add(server->schedule, 0, silc_idlist_purge, + (void *)i, i->timeout, 0, + SILC_TASK_TIMEOUT, SILC_TASK_PRI_LOW); +} /****************************************************************************** @@ -37,9 +116,7 @@ SilcServerEntry silc_idlist_add_server(SilcIDList id_list, char *server_name, int server_type, SilcServerID *id, SilcServerEntry router, - SilcCipher send_key, SilcCipher receive_key, - SilcPKCS pkcs, SilcHmac hmac, - SilcPublicKey public_key, void *connection) + void *connection) { SilcServerEntry server; @@ -50,15 +127,10 @@ silc_idlist_add_server(SilcIDList id_list, server->server_type = server_type; server->id = id; server->router = router; - server->send_key = send_key; - server->receive_key = receive_key; - server->pkcs = pkcs; - server->hmac = hmac; - server->public_key = public_key; server->connection = connection; - if (!silc_idcache_add(id_list->servers, server->server_name, SILC_ID_SERVER, - (void *)server->id, (void *)server, TRUE)) { + if (!silc_idcache_add(id_list->servers, server->server_name, + (void *)server->id, (void *)server, 0, NULL)) { silc_free(server); return NULL; } @@ -69,7 +141,8 @@ silc_idlist_add_server(SilcIDList id_list, /* Finds server by Server ID */ SilcServerEntry -silc_idlist_find_server_by_id(SilcIDList id_list, SilcServerID *id) +silc_idlist_find_server_by_id(SilcIDList id_list, SilcServerID *id, + bool registered, SilcIDCacheEntry *ret_entry) { SilcIDCacheEntry id_cache = NULL; SilcServerEntry server; @@ -77,14 +150,104 @@ silc_idlist_find_server_by_id(SilcIDList id_list, SilcServerID *id) if (!id) return NULL; - SILC_LOG_DEBUG(("Finding server by ID")); + SILC_LOG_DEBUG(("Server ID (%s)", + silc_id_render(id, SILC_ID_SERVER))); if (!silc_idcache_find_by_id_one(id_list->servers, (void *)id, - SILC_ID_SERVER, &id_cache)) + &id_cache)) return NULL; server = (SilcServerEntry)id_cache->context; + if (server && registered && + !(server->data.status & SILC_IDLIST_STATUS_REGISTERED)) + return NULL; + + if (ret_entry) + *ret_entry = id_cache; + + SILC_LOG_DEBUG(("Found")); + + return server; +} + +/* Find server by name */ + +SilcServerEntry +silc_idlist_find_server_by_name(SilcIDList id_list, char *name, + bool registered, SilcIDCacheEntry *ret_entry) +{ + SilcIDCacheEntry id_cache = NULL; + SilcServerEntry server; + + SILC_LOG_DEBUG(("Server by name `%s'", name)); + + if (!silc_idcache_find_by_name_one(id_list->servers, name, &id_cache)) + return NULL; + + server = (SilcServerEntry)id_cache->context; + + if (server && registered && + !(server->data.status & SILC_IDLIST_STATUS_REGISTERED)) + return NULL; + + if (ret_entry) + *ret_entry = id_cache; + + SILC_LOG_DEBUG(("Found")); + + return server; +} + +/* Find server by connection parameters, hostname and port */ + +SilcServerEntry +silc_idlist_find_server_by_conn(SilcIDList id_list, char *hostname, + int port, bool registered, + SilcIDCacheEntry *ret_entry) +{ + SilcIDCacheList list = NULL; + SilcIDCacheEntry id_cache = NULL; + SilcServerEntry server = NULL; + SilcSocketConnection sock; + + SILC_LOG_DEBUG(("Server by hostname %s and port %d", hostname, port)); + + if (!silc_idcache_get_all(id_list->servers, &list)) + return NULL; + + if (!silc_idcache_list_first(list, &id_cache)) { + silc_idcache_list_free(list); + return NULL; + } + + while (id_cache) { + server = (SilcServerEntry)id_cache->context; + sock = (SilcSocketConnection)server->connection; + + if (sock && ((sock->hostname && !strcasecmp(sock->hostname, hostname)) || + (sock->ip && !strcasecmp(sock->ip, hostname))) + && server->id->port == htons(port)) + break; + + id_cache = NULL; + server = NULL; + + if (!silc_idcache_list_next(list, &id_cache)) + break; + } + + silc_idcache_list_free(list); + + if (server && registered && + !(server->data.status & SILC_IDLIST_STATUS_REGISTERED)) + return NULL; + + if (ret_entry) + *ret_entry = id_cache; + + SILC_LOG_DEBUG(("Found")); + return server; } @@ -103,17 +266,53 @@ silc_idlist_replace_server_id(SilcIDList id_list, SilcServerID *old_id, SILC_LOG_DEBUG(("Replacing Server ID")); if (!silc_idcache_find_by_id_one(id_list->servers, (void *)old_id, - SILC_ID_SERVER, &id_cache)) + &id_cache)) return NULL; server = (SilcServerEntry)id_cache->context; + + /* Remove the old entry and add a new one */ + + silc_idcache_del_by_id(id_list->servers, (void *)server->id); + silc_free(server->id); server->id = new_id; - id_cache->id = (void *)new_id; + + silc_idcache_add(id_list->servers, server->server_name, server->id, + server, 0, NULL); + + SILC_LOG_DEBUG(("Found")); return server; } +/* Removes and free's server entry from ID list */ + +int silc_idlist_del_server(SilcIDList id_list, SilcServerEntry entry) +{ + if (entry) { + /* Remove from cache */ + if (!silc_idcache_del_by_context(id_list->servers, entry)) + return FALSE; + + SILC_LOG_DEBUG(("Deleting server %s id %s", entry->server_name ? + entry->server_name : "", + entry->id ? + silc_id_render(entry->id, SILC_ID_SERVER) : "")); + + /* Free data */ + silc_free(entry->server_name); + silc_free(entry->id); + silc_free(entry->server_info); + + memset(entry, 'F', sizeof(*entry)); + silc_free(entry); + return TRUE; + } + + return FALSE; +} + /****************************************************************************** Client entry functions @@ -122,15 +321,18 @@ silc_idlist_replace_server_id(SilcIDList id_list, SilcServerID *old_id, /* Add new client entry. This adds the client entry to ID cache system and returns the allocated client entry or NULL on error. This is - called when new client connection is accepted to the server. */ + called when new client connection is accepted to the server. If The + `router' is provided then the all server routines assume that the client + is not directly connected local client but it has router set and is + remote. If this is the case then `connection' must be NULL. If, on the + other hand, the `connection' is provided then the client is assumed + to be directly connected local client and `router' must be NULL. */ SilcClientEntry -silc_idlist_add_client(SilcIDList id_list, char *nickname, char *username, +silc_idlist_add_client(SilcIDList id_list, char *nickname, char *username, char *userinfo, SilcClientID *id, - SilcServerEntry router, - SilcCipher send_key, SilcCipher receive_key, - SilcPKCS pkcs, SilcHmac hmac, - SilcPublicKey public_key, void *connection) + SilcServerEntry router, void *connection, + int expire) { SilcClientEntry client; @@ -142,15 +344,13 @@ silc_idlist_add_client(SilcIDList id_list, char *nickname, char *username, client->userinfo = userinfo; client->id = id; client->router = router; - client->send_key = send_key; - client->receive_key = receive_key; - client->pkcs = pkcs; - client->hmac = hmac; - client->public_key = public_key; client->connection = connection; + client->channels = silc_hash_table_alloc(3, silc_hash_ptr, NULL, + NULL, NULL, NULL, NULL, TRUE); - if (!silc_idcache_add(id_list->clients, client->nickname, SILC_ID_CLIENT, - (void *)client->id, (void *)client, TRUE)) { + if (!silc_idcache_add(id_list->clients, nickname, (void *)client->id, + (void *)client, expire, NULL)) { + silc_hash_table_free(client->channels); silc_free(client); return NULL; } @@ -159,157 +359,121 @@ silc_idlist_add_client(SilcIDList id_list, char *nickname, char *username, } /* Free client entry. This free's everything and removes the entry - from ID cache. */ + from ID cache. Call silc_idlist_del_data before calling this one. */ -void silc_idlist_del_client(SilcIDList id_list, SilcClientEntry entry) +int silc_idlist_del_client(SilcIDList id_list, SilcClientEntry entry) { + SILC_LOG_DEBUG(("Start")); + if (entry) { /* Remove from cache */ - if (entry->id) - silc_idcache_del_by_id(id_list->clients, SILC_ID_CLIENT, - (void *)entry->id); + if (!silc_idcache_del_by_context(id_list->clients, entry)) + return FALSE; + + assert(!silc_hash_table_count(entry->channels)); /* Free data */ - if (entry->nickname) - silc_free(entry->nickname); - if (entry->username) - silc_free(entry->username); - if (entry->userinfo) - silc_free(entry->userinfo); - if (entry->id) - silc_free(entry->id); - if (entry->send_key) - silc_cipher_free(entry->send_key); - if (entry->receive_key) - silc_cipher_free(entry->receive_key); - if (entry->pkcs) - silc_pkcs_free(entry->pkcs); - if (entry->public_key) - silc_pkcs_public_key_free(entry->public_key); - if (entry->hmac) - silc_hmac_free(entry->hmac); + silc_free(entry->nickname); + silc_free(entry->servername); + silc_free(entry->username); + silc_free(entry->userinfo); + silc_free(entry->id); + silc_free(entry->attrs); + silc_hash_table_free(entry->channels); + + memset(entry, 'F', sizeof(*entry)); + silc_free(entry); + + return TRUE; } + + return FALSE; } /* Returns all clients matching requested nickname. Number of clients is returned to `clients_count'. Caller must free the returned table. */ -SilcClientEntry * -silc_idlist_get_clients_by_nickname(SilcIDList id_list, char *nickname, - char *server, unsigned int *clients_count) +int silc_idlist_get_clients_by_nickname(SilcIDList id_list, char *nickname, + char *server, + SilcClientEntry **clients, + SilcUInt32 *clients_count) { SilcIDCacheList list = NULL; SilcIDCacheEntry id_cache = NULL; - SilcClientEntry *clients; - int i; - if (!silc_idcache_find_by_data(id_list->clients, nickname, &list)) - return NULL; + SILC_LOG_DEBUG(("Start")); + + if (!silc_idcache_find_by_name(id_list->clients, nickname, &list)) + return FALSE; - clients = silc_calloc(silc_idcache_list_count(list), sizeof(*clients)); + *clients = silc_realloc(*clients, + (silc_idcache_list_count(list) + *clients_count) * + sizeof(**clients)); - i = 0; silc_idcache_list_first(list, &id_cache); - clients[i++] = (SilcClientEntry)id_cache->context; + (*clients)[(*clients_count)++] = (SilcClientEntry)id_cache->context; while (silc_idcache_list_next(list, &id_cache)) - clients[i++] = (SilcClientEntry)id_cache->context; + (*clients)[(*clients_count)++] = (SilcClientEntry)id_cache->context; silc_idcache_list_free(list); - if (clients_count) - *clients_count = i; + SILC_LOG_DEBUG(("Found total %d clients", *clients_count)); - return clients; + return TRUE; } -/* Finds client entry by nickname. */ +/* Returns all clients matching requested nickname hash. Number of clients + is returned to `clients_count'. Caller must free the returned table. */ -SilcClientEntry -silc_idlist_find_client_by_nickname(SilcIDList id_list, char *nickname, - char *server) +int silc_idlist_get_clients_by_hash(SilcIDList id_list, char *nickname, + SilcHash md5hash, + SilcClientEntry **clients, + SilcUInt32 *clients_count) { SilcIDCacheList list = NULL; SilcIDCacheEntry id_cache = NULL; - SilcClientEntry client = NULL; - - SILC_LOG_DEBUG(("Finding client by nickname")); - - if (server) { - if (!silc_idcache_find_by_data(id_list->clients, nickname, &list)) - return NULL; - -#if 0 - while (silc_idcache_list_next(list, &id_cache)) { - client = (SilcClientEntry)id_cache->context; - - if (!strcmp(server, XXX, strlen(server))) - break; - - client = NULL; - } -#endif - - silc_idcache_list_free(list); - - if (!client) - return NULL; - } else { - if (!silc_idcache_find_by_data_one(id_list->clients, nickname, &id_cache)) - return NULL; - - client = (SilcClientEntry)id_cache->context; - } - - return client; -} - -/* Finds client by nickname hash. */ - -SilcClientEntry -silc_idlist_find_client_by_hash(SilcIDList id_list, char *nickname, - SilcHash md5hash) -{ - SilcIDCacheList list = NULL; - SilcIDCacheEntry id_cache = NULL; - SilcClientEntry client = NULL; unsigned char hash[32]; + SilcClientID client_id; + char nick[128 + 1]; - SILC_LOG_DEBUG(("Finding client by hash")); + SILC_LOG_DEBUG(("Start")); - silc_hash_make(md5hash, nickname, strlen(nickname), hash); + memset(nick, 0, sizeof(nick)); + silc_to_lower(nickname, nick, sizeof(nick) - 1); + silc_hash_make(md5hash, nick, strlen(nick), hash); - if (!silc_idcache_find_by_id(id_list->clients, SILC_ID_CACHE_ANY, - SILC_ID_CLIENT, &list)) - return NULL; + /* As the Client ID is hashed in the ID cache by hashing only the hash + from the Client ID, we can do a lookup with only the hash not the + other parts of the ID and get all the clients with that hash, ie. + with that nickname, as the hash is from the nickname. */ + memset(&client_id, 0, sizeof(client_id)); + memcpy(&client_id.hash, hash, sizeof(client_id.hash)); + if (!silc_idcache_find_by_id(id_list->clients, &client_id, &list)) + return FALSE; - if (!silc_idcache_list_first(list, &id_cache)) { - silc_idcache_list_free(list); - return NULL; - } + *clients = silc_realloc(*clients, + (silc_idcache_list_count(list) + *clients_count) * + sizeof(**clients)); - while (id_cache) { - client = (SilcClientEntry)id_cache->context; - - if (client && !SILC_ID_COMPARE_HASH(client->id, hash)) - break; - - id_cache = NULL; - client = NULL; + silc_idcache_list_first(list, &id_cache); + (*clients)[(*clients_count)++] = (SilcClientEntry)id_cache->context; - if (!silc_idcache_list_next(list, &id_cache)) - break; - } + while (silc_idcache_list_next(list, &id_cache)) + (*clients)[(*clients_count)++] = (SilcClientEntry)id_cache->context; silc_idcache_list_free(list); + + SILC_LOG_DEBUG(("Found total %d clients", *clients_count)); - return client; + return TRUE; } /* Finds client by Client ID */ SilcClientEntry -silc_idlist_find_client_by_id(SilcIDList id_list, SilcClientID *id) +silc_idlist_find_client_by_id(SilcIDList id_list, SilcClientID *id, + bool registered, SilcIDCacheEntry *ret_entry) { SilcIDCacheEntry id_cache = NULL; SilcClientEntry client; @@ -317,22 +481,39 @@ silc_idlist_find_client_by_id(SilcIDList id_list, SilcClientID *id) if (!id) return NULL; - SILC_LOG_DEBUG(("Finding client by ID")); - - if (!silc_idcache_find_by_id_one(id_list->clients, (void *)id, - SILC_ID_CLIENT, &id_cache)) + SILC_LOG_DEBUG(("Client ID (%s)", + silc_id_render(id, SILC_ID_CLIENT))); + + /* Do extended search since the normal ID comparison function for + Client ID's compares only the hash from the Client ID and not the + entire ID. The silc_hash_client_id_compare compares the entire + Client ID as we want to find one specific Client ID. */ + if (!silc_idcache_find_by_id_one_ext(id_list->clients, (void *)id, + NULL, NULL, + silc_hash_client_id_compare, NULL, + &id_cache)) return NULL; client = (SilcClientEntry)id_cache->context; + if (client && registered && + !(client->data.status & SILC_IDLIST_STATUS_REGISTERED)) + return NULL; + + if (ret_entry) + *ret_entry = id_cache; + + SILC_LOG_DEBUG(("Found")); + return client; } /* Replaces old Client ID with new one */ SilcClientEntry -silc_idlist_replace_client_id(SilcIDList id_list, SilcClientID *old_id, - SilcClientID *new_id) +silc_idlist_replace_client_id(SilcServer server, + SilcIDList id_list, SilcClientID *old_id, + SilcClientID *new_id, const char *nickname) { SilcIDCacheEntry id_cache = NULL; SilcClientEntry client; @@ -342,18 +523,68 @@ silc_idlist_replace_client_id(SilcIDList id_list, SilcClientID *old_id, SILC_LOG_DEBUG(("Replacing Client ID")); - if (!silc_idcache_find_by_id_one(id_list->clients, (void *)old_id, - SILC_ID_CLIENT, &id_cache)) + /* Do extended search since the normal ID comparison function for + Client ID's compares only the hash from the Client ID and not the + entire ID. The silc_hash_client_id_compare compares the entire + Client ID as we want to find one specific Client ID. */ + if (!silc_idcache_find_by_id_one_ext(id_list->clients, (void *)old_id, + NULL, NULL, + silc_hash_client_id_compare, NULL, + &id_cache)) return NULL; client = (SilcClientEntry)id_cache->context; + + /* Remove the old entry and add a new one */ + + if (!silc_idcache_del_by_context(id_list->clients, client)) + return NULL; + + /* Check if anyone is watching old nickname */ + if (server->server_type == SILC_ROUTER) + silc_server_check_watcher_list(server, client, nickname, + SILC_NOTIFY_TYPE_NICK_CHANGE); + silc_free(client->id); + silc_free(client->nickname); client->id = new_id; - id_cache->id = (void *)new_id; + client->nickname = nickname ? strdup(nickname) : NULL; + + /* Check if anyone is watching new nickname */ + if (server->server_type == SILC_ROUTER) + silc_server_check_watcher_list(server, client, nickname, + SILC_NOTIFY_TYPE_NICK_CHANGE); + + if (!silc_idcache_add(id_list->clients, client->nickname, client->id, + client, 0, NULL)) + return NULL; + + SILC_LOG_DEBUG(("Replaced")); return client; } +/* Client cache entry destructor that is called when the cache is purged. */ + +void silc_idlist_client_destructor(SilcIDCache cache, + SilcIDCacheEntry entry) +{ + SilcClientEntry client; + + client = (SilcClientEntry)entry->context; + if (client) { + assert(!silc_hash_table_count(client->channels)); + silc_free(client->nickname); + silc_free(client->username); + silc_free(client->userinfo); + silc_free(client->id); + silc_free(client->attrs); + silc_hash_table_free(client->channels); + + memset(client, 'A', sizeof(*client)); + silc_free(client); + } +} /****************************************************************************** @@ -367,22 +598,34 @@ silc_idlist_replace_client_id(SilcIDList id_list, SilcClientID *old_id, SilcChannelEntry silc_idlist_add_channel(SilcIDList id_list, char *channel_name, int mode, SilcChannelID *id, SilcServerEntry router, - SilcCipher channel_key) + SilcCipher channel_key, SilcHmac hmac, + int expire) { SilcChannelEntry channel; + SILC_LOG_DEBUG(("Adding new channel %s", channel_name)); + channel = silc_calloc(1, sizeof(*channel)); channel->channel_name = channel_name; channel->mode = mode; channel->id = id; channel->router = router; channel->channel_key = channel_key; - silc_list_init(channel->user_list, struct SilcChannelClientEntryStruct, - next); + channel->hmac = hmac; + channel->created = channel->updated = time(0); + if (!channel->hmac) + if (!silc_hmac_alloc(SILC_DEFAULT_HMAC, NULL, &channel->hmac)) { + silc_free(channel); + return NULL; + } + + channel->user_list = silc_hash_table_alloc(3, silc_hash_ptr, NULL, NULL, + NULL, NULL, NULL, TRUE); if (!silc_idcache_add(id_list->channels, channel->channel_name, - SILC_ID_CHANNEL, (void *)channel->id, - (void *)channel, TRUE)) { + (void *)channel->id, (void *)channel, expire, NULL)) { + silc_hmac_free(channel->hmac); + silc_hash_table_free(channel->user_list); silc_free(channel); return NULL; } @@ -390,86 +633,208 @@ silc_idlist_add_channel(SilcIDList id_list, char *channel_name, int mode, return channel; } +/* Foreach callbcak to free all users from the channel when deleting a + channel entry. */ + +static void silc_idlist_del_channel_foreach(void *key, void *context, + void *user_context) +{ + SilcChannelClientEntry chl = (SilcChannelClientEntry)context; + + SILC_LOG_DEBUG(("Removing client %s from channel %s", + chl->client->nickname ? chl->client->nickname : + (unsigned char *)"", chl->channel->channel_name)); + + /* Remove the context from the client's channel hash table as that + table and channel's user_list hash table share this same context. */ + silc_hash_table_del(chl->client->channels, chl->channel); + silc_free(chl); +} + /* Free channel entry. This free's everything. */ -void silc_idlist_del_channel(SilcIDList id_list, SilcChannelEntry entry) +int silc_idlist_del_channel(SilcIDList id_list, SilcChannelEntry entry) { if (entry) { - SilcChannelClientEntry chl; - /* Remove from cache */ - if (entry->id) - silc_idcache_del_by_id(id_list->channels, SILC_ID_CHANNEL, - (void *)entry->id); + if (!silc_idcache_del_by_context(id_list->channels, entry)) + return FALSE; + + SILC_LOG_DEBUG(("Deleting channel %s", entry->channel_name)); + + /* Free all client entrys from the users list. The silc_hash_table_free + will free all the entries so they are not freed at the foreach + callback. */ + silc_hash_table_foreach(entry->user_list, silc_idlist_del_channel_foreach, + NULL); + silc_hash_table_free(entry->user_list); /* Free data */ - if (entry->channel_name) - silc_free(entry->channel_name); - if (entry->id) - silc_free(entry->id); - if (entry->topic) - silc_free(entry->topic); + silc_free(entry->channel_name); + silc_free(entry->id); + silc_free(entry->topic); + + if (entry->invite_list) + silc_hash_table_free(entry->invite_list); + if (entry->ban_list) + silc_hash_table_free(entry->ban_list); + if (entry->channel_key) silc_cipher_free(entry->channel_key); if (entry->key) { memset(entry->key, 0, entry->key_len / 8); silc_free(entry->key); } - memset(entry->iv, 0, sizeof(entry->iv)); - - silc_list_start(entry->user_list); - while ((chl = silc_list_get(entry->user_list)) != SILC_LIST_END) { - silc_list_del(entry->user_list, chl); - silc_free(chl); - } + silc_free(entry->cipher); + if (entry->hmac) + silc_hmac_free(entry->hmac); + silc_free(entry->hmac_name); + silc_free(entry->rekey); + if (entry->founder_key) + silc_pkcs_public_key_free(entry->founder_key); + + memset(entry, 'F', sizeof(*entry)); + silc_free(entry); + return TRUE; } + + return FALSE; } /* Finds channel by channel name. Channel names are unique and they are not case-sensitive. */ SilcChannelEntry -silc_idlist_find_channel_by_name(SilcIDList id_list, char *name) +silc_idlist_find_channel_by_name(SilcIDList id_list, char *name, + SilcIDCacheEntry *ret_entry) { - SilcIDCacheList list = NULL; SilcIDCacheEntry id_cache = NULL; - SilcChannelEntry channel; - SILC_LOG_DEBUG(("Finding channel by name")); + SILC_LOG_DEBUG(("Channel by name %s", name)); - if (!silc_idcache_find_by_data_loose(id_list->channels, name, &list)) + if (!silc_idcache_find_by_name_one(id_list->channels, name, &id_cache)) return NULL; - - if (!silc_idcache_list_first(list, &id_cache)) { - silc_idcache_list_free(list); + + if (ret_entry) + *ret_entry = id_cache; + + SILC_LOG_DEBUG(("Found")); + + /* Touch channel */ + ((SilcChannelEntry)id_cache->context)->updated = time(NULL); + + return id_cache->context; +} + +/* Finds channel by Channel ID. */ + +SilcChannelEntry +silc_idlist_find_channel_by_id(SilcIDList id_list, SilcChannelID *id, + SilcIDCacheEntry *ret_entry) +{ + SilcIDCacheEntry id_cache = NULL; + SilcChannelEntry channel; + + if (!id) + return NULL; + + SILC_LOG_DEBUG(("Channel ID (%s)", + silc_id_render(id, SILC_ID_CHANNEL))); + + if (!silc_idcache_find_by_id_one(id_list->channels, (void *)id, &id_cache)) return NULL; - } channel = (SilcChannelEntry)id_cache->context; - silc_idcache_list_free(list); + if (ret_entry) + *ret_entry = id_cache; + + SILC_LOG_DEBUG(("Found")); + + /* Touch channel */ + channel->updated = time(NULL); return channel; } -/* Finds channel by Channel ID. */ +/* Replaces old Channel ID with new one. This is done when router forces + normal server to change Channel ID. */ SilcChannelEntry -silc_idlist_find_channel_by_id(SilcIDList id_list, SilcChannelID *id) +silc_idlist_replace_channel_id(SilcIDList id_list, SilcChannelID *old_id, + SilcChannelID *new_id) { SilcIDCacheEntry id_cache = NULL; SilcChannelEntry channel; - if (!id) + if (!old_id || !new_id) return NULL; - SILC_LOG_DEBUG(("Finding channel by ID")); + SILC_LOG_DEBUG(("Replacing Channel ID")); - if (!silc_idcache_find_by_id_one(id_list->channels, (void *)id, - SILC_ID_CHANNEL, &id_cache)) + if (!silc_idcache_find_by_id_one(id_list->channels, (void *)old_id, + &id_cache)) return NULL; channel = (SilcChannelEntry)id_cache->context; + /* Remove the old entry and add a new one */ + + silc_idcache_del_by_id(id_list->channels, (void *)channel->id); + + silc_free(channel->id); + channel->id = new_id; + + silc_idcache_add(id_list->channels, channel->channel_name, channel->id, + channel, 0, NULL); + + SILC_LOG_DEBUG(("Replaced")); + + /* Touch channel */ + channel->updated = time(NULL); + return channel; } + +/* Returns channels from the ID list. If the `channel_id' is NULL then + all channels are returned. */ + +SilcChannelEntry * +silc_idlist_get_channels(SilcIDList id_list, SilcChannelID *channel_id, + SilcUInt32 *channels_count) +{ + SilcIDCacheList list = NULL; + SilcIDCacheEntry id_cache = NULL; + SilcChannelEntry *channels = NULL; + int i = 0; + + SILC_LOG_DEBUG(("Start")); + + if (!channel_id) { + if (!silc_idcache_get_all(id_list->channels, &list)) + return NULL; + + channels = silc_calloc(silc_idcache_list_count(list), sizeof(*channels)); + + i = 0; + silc_idcache_list_first(list, &id_cache); + channels[i++] = (SilcChannelEntry)id_cache->context; + + while (silc_idcache_list_next(list, &id_cache)) + channels[i++] = (SilcChannelEntry)id_cache->context; + + silc_idcache_list_free(list); + } else { + if (!silc_idcache_find_by_id_one(id_list->channels, channel_id, &id_cache)) + return NULL; + + i = 1; + channels = silc_calloc(1, sizeof(*channels)); + channels[0] = (SilcChannelEntry)id_cache->context; + } + + if (channels_count) + *channels_count = i; + + return channels; +}