Added one more assert in client entry freeing.
[silc.git] / apps / silcd / idlist.c
1 /*
2
3   idlist.c
4
5   Author: Pekka Riikonen <priikone@silcnet.org>
6
7   Copyright (C) 1997 - 2002 Pekka Riikonen
8
9   This program is free software; you can redistribute it and/or modify
10   it under the terms of the GNU General Public License as published by
11   the Free Software Foundation; either version 2 of the License, or
12   (at your option) any later version.
13   
14   This program is distributed in the hope that it will be useful,
15   but WITHOUT ANY WARRANTY; without even the implied warranty of
16   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17   GNU General Public License for more details.
18
19 */
20 /* $Id$ */
21
22 #include "serverincludes.h"
23 #include "idlist.h"
24 #include "server_internal.h"
25
26 /******************************************************************************
27
28                              Common functions
29
30 ******************************************************************************/
31
32 /* This function is used to add keys and stuff to common ID entry data
33    structure. */
34
35 void silc_idlist_add_data(void *entry, SilcIDListData idata)
36 {
37   SilcIDListData data = (SilcIDListData)entry;
38   data->send_key = idata->send_key;
39   data->receive_key = idata->receive_key;
40   data->hmac_send = idata->hmac_send;
41   data->hmac_receive = idata->hmac_receive;
42   data->psn_send = idata->psn_send;
43   data->psn_receive = idata->psn_receive;
44   data->hash = idata->hash;
45   data->public_key = idata->public_key;
46   memcpy(data->fingerprint, idata->fingerprint, sizeof(data->fingerprint));
47   data->rekey = idata->rekey;
48   data->last_receive = idata->last_receive;
49   data->last_sent = idata->last_sent;
50   data->status = idata->status;
51
52   data->created = time(0);      /* Update creation time */
53 }
54
55 /* Free's all data in the common ID entry data structure. */
56
57 void silc_idlist_del_data(void *entry)
58 {
59   SilcIDListData idata = (SilcIDListData)entry;
60   if (idata->send_key)
61     silc_cipher_free(idata->send_key);
62   if (idata->receive_key)
63     silc_cipher_free(idata->receive_key);
64   if (idata->rekey) {
65     if (idata->rekey->send_enc_key) {
66       memset(idata->rekey->send_enc_key, 0, idata->rekey->enc_key_len);
67       silc_free(idata->rekey->send_enc_key);
68     }
69     silc_free(idata->rekey);
70   }
71   if (idata->hmac_send)
72     silc_hmac_free(idata->hmac_send);
73   if (idata->hmac_receive)
74     silc_hmac_free(idata->hmac_receive);
75   if (idata->hash)
76     silc_hash_free(idata->hash);
77   if (idata->public_key)
78     silc_pkcs_public_key_free(idata->public_key);
79
80   idata->send_key = NULL;
81   idata->receive_key = NULL;
82   idata->rekey = NULL;
83   idata->hmac_send = NULL;
84   idata->hmac_receive = NULL;
85   idata->hash = NULL;
86   idata->public_key = NULL;
87 }
88
89 /* Purges ID cache */
90
91 SILC_TASK_CALLBACK_GLOBAL(silc_idlist_purge)
92 {
93   SilcIDListPurge i = (SilcIDListPurge)context;
94
95   SILC_LOG_DEBUG(("Purging cache"));
96
97   silc_idcache_purge(i->cache);
98   silc_schedule_task_add(i->schedule, 0, 
99                          silc_idlist_purge,
100                          (void *)i, i->timeout, 0,
101                          SILC_TASK_TIMEOUT, SILC_TASK_PRI_LOW);
102 }
103
104 /******************************************************************************
105
106                           Server entry functions
107
108 ******************************************************************************/
109
110 /* Add new server entry. This adds the new server entry to ID cache and
111    returns the allocated entry object or NULL on error. This is called
112    when new server connects to us. We also add ourselves to cache with
113    this function. */
114
115 SilcServerEntry 
116 silc_idlist_add_server(SilcIDList id_list, 
117                        char *server_name, int server_type,
118                        SilcServerID *id, SilcServerEntry router,
119                        void *connection)
120 {
121   SilcServerEntry server;
122
123   SILC_LOG_DEBUG(("Adding new server entry"));
124
125   server = silc_calloc(1, sizeof(*server));
126   server->server_name = server_name;
127   server->server_type = server_type;
128   server->id = id;
129   server->router = router;
130   server->connection = connection;
131
132   if (!silc_idcache_add(id_list->servers, server->server_name, 
133                         (void *)server->id, (void *)server, 0, NULL)) {
134     silc_free(server);
135     return NULL;
136   }
137
138   return server;
139 }
140
141 /* Finds server by Server ID */
142
143 SilcServerEntry
144 silc_idlist_find_server_by_id(SilcIDList id_list, SilcServerID *id,
145                               bool registered, SilcIDCacheEntry *ret_entry)
146 {
147   SilcIDCacheEntry id_cache = NULL;
148   SilcServerEntry server;
149
150   if (!id)
151     return NULL;
152
153   SILC_LOG_DEBUG(("Server ID (%s)",
154                   silc_id_render(id, SILC_ID_SERVER)));
155
156   if (!silc_idcache_find_by_id_one(id_list->servers, (void *)id, 
157                                    &id_cache))
158     return NULL;
159
160   server = (SilcServerEntry)id_cache->context;
161
162   if (ret_entry)
163     *ret_entry = id_cache;
164
165   if (server && registered && 
166       !(server->data.status & SILC_IDLIST_STATUS_REGISTERED))
167     return NULL;
168
169   SILC_LOG_DEBUG(("Found"));
170
171   return server;
172 }
173
174 /* Find server by name */
175
176 SilcServerEntry
177 silc_idlist_find_server_by_name(SilcIDList id_list, char *name,
178                                 bool registered, SilcIDCacheEntry *ret_entry)
179 {
180   SilcIDCacheEntry id_cache = NULL;
181   SilcServerEntry server;
182
183   SILC_LOG_DEBUG(("Server by name `%s'", name));
184
185   if (!silc_idcache_find_by_name_one(id_list->servers, name, &id_cache))
186     return NULL;
187
188   server = (SilcServerEntry)id_cache->context;
189   
190   if (ret_entry)
191     *ret_entry = id_cache;
192
193   if (server && registered &&
194       !(server->data.status & SILC_IDLIST_STATUS_REGISTERED))
195     return NULL;
196
197   SILC_LOG_DEBUG(("Found"));
198
199   return server;
200 }
201
202 /* Find server by connection parameters, hostname and port */
203
204 SilcServerEntry
205 silc_idlist_find_server_by_conn(SilcIDList id_list, char *hostname,
206                                 int port, bool registered,
207                                 SilcIDCacheEntry *ret_entry)
208 {
209   SilcIDCacheList list = NULL;
210   SilcIDCacheEntry id_cache = NULL;
211   SilcServerEntry server = NULL;
212   SilcSocketConnection sock;
213  
214   SILC_LOG_DEBUG(("Server by hostname %s and port %d", hostname, port));
215
216   if (!silc_idcache_get_all(id_list->servers, &list))
217     return NULL;
218
219   if (!silc_idcache_list_first(list, &id_cache)) {
220     silc_idcache_list_free(list);
221     return NULL;
222   }
223
224   while (id_cache) {
225     server = (SilcServerEntry)id_cache->context;
226     sock = (SilcSocketConnection)server->connection;
227     
228     if (sock && ((sock->hostname && !strcasecmp(sock->hostname, hostname)) ||
229                  (sock->ip && !strcasecmp(sock->ip, hostname)))
230         && sock->port == port)
231       break;
232
233     id_cache = NULL;
234     server = NULL;
235
236     if (!silc_idcache_list_next(list, &id_cache))
237       break;
238   }
239   
240   silc_idcache_list_free(list);
241
242   if (ret_entry)
243     *ret_entry = id_cache;
244
245   if (server && registered &&
246       !(server->data.status & SILC_IDLIST_STATUS_REGISTERED))
247     return NULL;
248
249   SILC_LOG_DEBUG(("Found"));
250
251   return server;
252 }
253
254 /* Replaces old Server ID with new one */ 
255
256 SilcServerEntry
257 silc_idlist_replace_server_id(SilcIDList id_list, SilcServerID *old_id,
258                               SilcServerID *new_id)
259 {
260   SilcIDCacheEntry id_cache = NULL;
261   SilcServerEntry server;
262
263   if (!old_id || !new_id)
264     return NULL;
265
266   SILC_LOG_DEBUG(("Replacing Server ID"));
267
268   if (!silc_idcache_find_by_id_one(id_list->servers, (void *)old_id, 
269                                    &id_cache))
270     return NULL;
271
272   server = (SilcServerEntry)id_cache->context;
273
274   /* Remove the old entry and add a new one */
275
276   silc_idcache_del_by_id(id_list->servers, (void *)server->id);
277
278   silc_free(server->id);
279   server->id = new_id;
280
281   silc_idcache_add(id_list->servers, server->server_name, server->id, 
282                    server, 0, NULL);
283
284   SILC_LOG_DEBUG(("Found"));
285
286   return server;
287 }
288
289 /* Removes and free's server entry from ID list */
290
291 int silc_idlist_del_server(SilcIDList id_list, SilcServerEntry entry)
292 {
293   if (entry) {
294     /* Remove from cache */
295     if (!silc_idcache_del_by_context(id_list->servers, entry))
296       return FALSE;
297
298     SILC_LOG_DEBUG(("Deleting server %s", entry->server_name ?
299                     entry->server_name : ""));
300
301     /* Free data */
302     silc_free(entry->server_name);
303     silc_free(entry->id);
304     silc_free(entry->server_info);
305
306     memset(entry, 'F', sizeof(*entry));
307     silc_free(entry);
308     return TRUE;
309   }
310
311   return FALSE;
312 }
313
314 /******************************************************************************
315
316                           Client entry functions
317
318 ******************************************************************************/
319
320 /* Add new client entry. This adds the client entry to ID cache system
321    and returns the allocated client entry or NULL on error.  This is
322    called when new client connection is accepted to the server. If The
323    `router' is provided then the all server routines assume that the client
324    is not directly connected local client but it has router set and is
325    remote.  If this is the case then `connection' must be NULL.  If, on the
326    other hand, the `connection' is provided then the client is assumed
327    to be directly connected local client and `router' must be NULL. */
328
329 SilcClientEntry
330 silc_idlist_add_client(SilcIDList id_list, char *nickname, char *username, 
331                        char *userinfo, SilcClientID *id, 
332                        SilcServerEntry router, void *connection,
333                        int expire)
334 {
335   SilcClientEntry client;
336
337   SILC_LOG_DEBUG(("Adding new client entry"));
338
339   client = silc_calloc(1, sizeof(*client));
340   client->nickname = nickname;
341   client->username = username;
342   client->userinfo = userinfo;
343   client->id = id;
344   client->router = router;
345   client->connection = connection;
346   client->channels = silc_hash_table_alloc(3, silc_hash_ptr, NULL,
347                                            NULL, NULL, NULL, NULL, TRUE);
348
349   if (!silc_idcache_add(id_list->clients, nickname, (void *)client->id, 
350                         (void *)client, expire, NULL)) {
351     silc_hash_table_free(client->channels);
352     silc_free(client);
353     return NULL;
354   }
355
356   return client;
357 }
358
359 /* Free client entry. This free's everything and removes the entry
360    from ID cache. Call silc_idlist_del_data before calling this one. */
361
362 int silc_idlist_del_client(SilcIDList id_list, SilcClientEntry entry)
363 {
364   SILC_LOG_DEBUG(("Start"));
365
366   if (entry) {
367     /* Remove from cache */
368     if (!silc_idcache_del_by_context(id_list->clients, entry))
369       return FALSE;
370
371     assert(!silc_hash_table_count(entry->channels));
372
373     /* Free data */
374     silc_free(entry->nickname);
375     silc_free(entry->servername);
376     silc_free(entry->username);
377     silc_free(entry->userinfo);
378     silc_free(entry->id);
379     silc_hash_table_free(entry->channels);
380
381     memset(entry, 'F', sizeof(*entry));
382     silc_free(entry);
383
384     return TRUE;
385   }
386
387   return FALSE;
388 }
389
390 /* Returns all clients matching requested nickname. Number of clients is
391    returned to `clients_count'. Caller must free the returned table. */
392
393 int silc_idlist_get_clients_by_nickname(SilcIDList id_list, char *nickname,
394                                         char *server, 
395                                         SilcClientEntry **clients,
396                                         SilcUInt32 *clients_count)
397 {
398   SilcIDCacheList list = NULL;
399   SilcIDCacheEntry id_cache = NULL;
400
401   SILC_LOG_DEBUG(("Start"));
402
403   if (!silc_idcache_find_by_name(id_list->clients, nickname, &list))
404     return FALSE;
405
406   *clients = silc_realloc(*clients, 
407                           (silc_idcache_list_count(list) + *clients_count) * 
408                           sizeof(**clients));
409
410   silc_idcache_list_first(list, &id_cache);
411   (*clients)[(*clients_count)++] = (SilcClientEntry)id_cache->context;
412
413   while (silc_idcache_list_next(list, &id_cache))
414     (*clients)[(*clients_count)++] = (SilcClientEntry)id_cache->context;
415   
416   silc_idcache_list_free(list);
417   
418   SILC_LOG_DEBUG(("Found total %d clients", *clients_count));
419
420   return TRUE;
421 }
422
423 /* Returns all clients matching requested nickname hash. Number of clients
424    is returned to `clients_count'. Caller must free the returned table. */
425
426 int silc_idlist_get_clients_by_hash(SilcIDList id_list, char *nickname,
427                                     SilcHash md5hash,
428                                     SilcClientEntry **clients,
429                                     SilcUInt32 *clients_count)
430 {
431   SilcIDCacheList list = NULL;
432   SilcIDCacheEntry id_cache = NULL;
433   unsigned char hash[32];
434   SilcClientID client_id;
435   char nick[128 + 1];
436
437   SILC_LOG_DEBUG(("Start"));
438
439   memset(nick, 0, sizeof(nick));
440   silc_to_lower(nickname, nick, sizeof(nick) - 1);
441   silc_hash_make(md5hash, nick, strlen(nick), hash);
442
443   /* As the Client ID is hashed in the ID cache by hashing only the hash
444      from the Client ID, we can do a lookup with only the hash not the
445      other parts of the ID and get all the clients with that hash, ie.
446      with that nickname, as the hash is from the nickname. */
447   memset(&client_id, 0, sizeof(client_id));
448   memcpy(&client_id.hash, hash, sizeof(client_id.hash));
449   if (!silc_idcache_find_by_id(id_list->clients, &client_id, &list))
450     return FALSE;
451
452   *clients = silc_realloc(*clients, 
453                           (silc_idcache_list_count(list) + *clients_count) * 
454                           sizeof(**clients));
455
456   silc_idcache_list_first(list, &id_cache);
457   (*clients)[(*clients_count)++] = (SilcClientEntry)id_cache->context;
458
459   while (silc_idcache_list_next(list, &id_cache))
460     (*clients)[(*clients_count)++] = (SilcClientEntry)id_cache->context;
461   
462   silc_idcache_list_free(list);
463   
464   SILC_LOG_DEBUG(("Found total %d clients", *clients_count));
465
466   return TRUE;
467 }
468
469 /* Finds client by Client ID */
470
471 SilcClientEntry
472 silc_idlist_find_client_by_id(SilcIDList id_list, SilcClientID *id,
473                               bool registered, SilcIDCacheEntry *ret_entry)
474 {
475   SilcIDCacheEntry id_cache = NULL;
476   SilcClientEntry client;
477
478   if (!id)
479     return NULL;
480
481   SILC_LOG_DEBUG(("Client ID (%s)", 
482                   silc_id_render(id, SILC_ID_CLIENT)));
483
484   /* Do extended search since the normal ID comparison function for
485      Client ID's compares only the hash from the Client ID and not the
486      entire ID. The silc_hash_client_id_compare compares the entire
487      Client ID as we want to find one specific Client ID. */
488   if (!silc_idcache_find_by_id_one_ext(id_list->clients, (void *)id, 
489                                        NULL, NULL, 
490                                        silc_hash_client_id_compare, NULL,
491                                        &id_cache))
492     return NULL;
493
494   client = (SilcClientEntry)id_cache->context;
495
496   if (ret_entry)
497     *ret_entry = id_cache;
498
499   if (client && registered &&
500       !(client->data.status & SILC_IDLIST_STATUS_REGISTERED))
501     return NULL;
502
503   SILC_LOG_DEBUG(("Found"));
504
505   return client;
506 }
507
508 /* Replaces old Client ID with new one */
509
510 SilcClientEntry
511 silc_idlist_replace_client_id(SilcServer server,
512                               SilcIDList id_list, SilcClientID *old_id,
513                               SilcClientID *new_id, const char *nickname)
514 {
515   SilcIDCacheEntry id_cache = NULL;
516   SilcClientEntry client;
517
518   if (!old_id || !new_id)
519     return NULL;
520
521   SILC_LOG_DEBUG(("Replacing Client ID"));
522
523   /* Do extended search since the normal ID comparison function for
524      Client ID's compares only the hash from the Client ID and not the
525      entire ID. The silc_hash_client_id_compare compares the entire
526      Client ID as we want to find one specific Client ID. */
527   if (!silc_idcache_find_by_id_one_ext(id_list->clients, (void *)old_id, 
528                                        NULL, NULL, 
529                                        silc_hash_client_id_compare, NULL,
530                                        &id_cache))
531     return NULL;
532
533   client = (SilcClientEntry)id_cache->context;
534
535   /* Remove the old entry and add a new one */
536
537   if (!silc_idcache_del_by_context(id_list->clients, client))
538     return NULL;
539
540   /* Check if anyone is watching old nickname */
541   if (server->server_type == SILC_ROUTER)
542     silc_server_check_watcher_list(server, client, nickname,
543                                    SILC_NOTIFY_TYPE_NICK_CHANGE);
544
545   silc_free(client->id);
546   silc_free(client->nickname);
547   client->id = new_id;
548   client->nickname = nickname ? strdup(nickname) : NULL;
549
550   /* Check if anyone is watching new nickname */
551   if (server->server_type == SILC_ROUTER)
552     silc_server_check_watcher_list(server, client, nickname,
553                                    SILC_NOTIFY_TYPE_NICK_CHANGE);
554
555   if (!silc_idcache_add(id_list->clients, client->nickname, client->id, 
556                         client, 0, NULL))
557     return NULL;
558
559   SILC_LOG_DEBUG(("Replaced"));
560
561   return client;
562 }
563
564 /* Client cache entry destructor that is called when the cache is purged. */
565
566 void silc_idlist_client_destructor(SilcIDCache cache,
567                                    SilcIDCacheEntry entry)
568 {
569   SilcClientEntry client;
570
571   client = (SilcClientEntry)entry->context;
572   if (client) {
573     assert(!silc_hash_table_count(client->channels));
574     silc_free(client->nickname);
575     silc_free(client->username);
576     silc_free(client->userinfo);
577     silc_free(client->id);
578     silc_hash_table_free(client->channels);
579
580     memset(client, 'A', sizeof(*client));
581     silc_free(client);
582   }
583 }
584
585 /******************************************************************************
586
587                           Channel entry functions
588
589 ******************************************************************************/
590
591 /* Add new channel entry. This add the new channel entry to the ID cache
592    system and returns the allocated entry or NULL on error. */
593
594 SilcChannelEntry
595 silc_idlist_add_channel(SilcIDList id_list, char *channel_name, int mode,
596                         SilcChannelID *id, SilcServerEntry router,
597                         SilcCipher channel_key, SilcHmac hmac,
598                         int expire)
599 {
600   SilcChannelEntry channel;
601
602   SILC_LOG_DEBUG(("Adding new channel %s", channel_name));
603
604   channel = silc_calloc(1, sizeof(*channel));
605   channel->channel_name = channel_name;
606   channel->mode = mode;
607   channel->id = id;
608   channel->router = router;
609   channel->channel_key = channel_key;
610   channel->hmac = hmac;
611   channel->created = channel->updated = time(0);
612   if (!channel->hmac)
613     if (!silc_hmac_alloc(SILC_DEFAULT_HMAC, NULL, &channel->hmac)) {
614       silc_free(channel);
615       return NULL;
616     }
617
618   channel->user_list = silc_hash_table_alloc(3, silc_hash_ptr, NULL, NULL,
619                                              NULL, NULL, NULL, TRUE);
620
621   if (!silc_idcache_add(id_list->channels, channel->channel_name, 
622                         (void *)channel->id, (void *)channel, expire, NULL)) {
623     silc_hmac_free(channel->hmac);
624     silc_hash_table_free(channel->user_list);
625     silc_free(channel);
626     return NULL;
627   }
628
629   return channel;
630 }
631
632 /* Foreach callbcak to free all users from the channel when deleting a
633    channel entry. */
634
635 static void silc_idlist_del_channel_foreach(void *key, void *context,
636                                             void *user_context)
637 {
638   SilcChannelClientEntry chl = (SilcChannelClientEntry)context;
639
640   SILC_LOG_DEBUG(("Removing client %s from channel %s",
641                   chl->client->nickname ? chl->client->nickname :
642                   (unsigned char *)"", chl->channel->channel_name));
643
644   /* Remove the context from the client's channel hash table as that
645      table and channel's user_list hash table share this same context. */
646   silc_hash_table_del(chl->client->channels, chl->channel);
647   silc_free(chl);
648 }
649
650 /* Free channel entry.  This free's everything. */
651
652 int silc_idlist_del_channel(SilcIDList id_list, SilcChannelEntry entry)
653 {
654   if (entry) {
655     /* Remove from cache */
656     if (!silc_idcache_del_by_context(id_list->channels, entry))
657       return FALSE;
658
659     SILC_LOG_DEBUG(("Deleting channel %s", entry->channel_name));
660
661     /* Free all client entrys from the users list. The silc_hash_table_free
662        will free all the entries so they are not freed at the foreach 
663        callback. */
664     silc_hash_table_foreach(entry->user_list, silc_idlist_del_channel_foreach,
665                             NULL);
666     silc_hash_table_free(entry->user_list);
667
668     /* Free data */
669     silc_free(entry->channel_name);
670     silc_free(entry->id);
671     silc_free(entry->topic);
672     if (entry->channel_key)
673       silc_cipher_free(entry->channel_key);
674     if (entry->key) {
675       memset(entry->key, 0, entry->key_len / 8);
676       silc_free(entry->key);
677     }
678     silc_free(entry->cipher);
679     if (entry->hmac)
680       silc_hmac_free(entry->hmac);
681     silc_free(entry->hmac_name);
682     silc_free(entry->rekey);
683     if (entry->founder_key)
684       silc_pkcs_public_key_free(entry->founder_key);
685
686     memset(entry, 'F', sizeof(*entry));
687     silc_free(entry);
688     return TRUE;
689   }
690
691   return FALSE;
692 }
693
694 /* Finds channel by channel name. Channel names are unique and they
695    are not case-sensitive. */
696
697 SilcChannelEntry
698 silc_idlist_find_channel_by_name(SilcIDList id_list, char *name,
699                                  SilcIDCacheEntry *ret_entry)
700 {
701   SilcIDCacheEntry id_cache = NULL;
702
703   SILC_LOG_DEBUG(("Channel by name %s", name));
704
705   if (!silc_idcache_find_by_name_one(id_list->channels, name, &id_cache))
706     return NULL;
707
708   if (ret_entry)
709     *ret_entry = id_cache;
710
711   SILC_LOG_DEBUG(("Found"));
712
713   /* Touch channel */
714   ((SilcChannelEntry)id_cache->context)->updated = time(NULL);
715
716   return id_cache->context;
717 }
718
719 /* Finds channel by Channel ID. */
720
721 SilcChannelEntry
722 silc_idlist_find_channel_by_id(SilcIDList id_list, SilcChannelID *id,
723                                SilcIDCacheEntry *ret_entry)
724 {
725   SilcIDCacheEntry id_cache = NULL;
726   SilcChannelEntry channel;
727
728   if (!id)
729     return NULL;
730
731   SILC_LOG_DEBUG(("Channel ID (%s)",
732                   silc_id_render(id, SILC_ID_CHANNEL)));
733
734   if (!silc_idcache_find_by_id_one(id_list->channels, (void *)id, &id_cache))
735     return NULL;
736
737   channel = (SilcChannelEntry)id_cache->context;
738
739   if (ret_entry)
740     *ret_entry = id_cache;
741
742   SILC_LOG_DEBUG(("Found"));
743
744   /* Touch channel */
745   channel->updated = time(NULL);
746
747   return channel;
748 }
749
750 /* Replaces old Channel ID with new one. This is done when router forces
751    normal server to change Channel ID. */
752
753 SilcChannelEntry
754 silc_idlist_replace_channel_id(SilcIDList id_list, SilcChannelID *old_id,
755                                SilcChannelID *new_id)
756 {
757   SilcIDCacheEntry id_cache = NULL;
758   SilcChannelEntry channel;
759
760   if (!old_id || !new_id)
761     return NULL;
762
763   SILC_LOG_DEBUG(("Replacing Channel ID"));
764
765   if (!silc_idcache_find_by_id_one(id_list->channels, (void *)old_id, 
766                                    &id_cache))
767     return NULL;
768
769   channel = (SilcChannelEntry)id_cache->context;
770
771   /* Remove the old entry and add a new one */
772
773   silc_idcache_del_by_id(id_list->channels, (void *)channel->id);
774
775   silc_free(channel->id);
776   channel->id = new_id;
777
778   silc_idcache_add(id_list->channels, channel->channel_name, channel->id, 
779                    channel, 0, NULL);
780
781   SILC_LOG_DEBUG(("Replaced"));
782
783   /* Touch channel */
784   channel->updated = time(NULL);
785
786   return channel;
787 }
788
789 /* Returns channels from the ID list. If the `channel_id' is NULL then
790    all channels are returned. */
791
792 SilcChannelEntry *
793 silc_idlist_get_channels(SilcIDList id_list, SilcChannelID *channel_id,
794                          SilcUInt32 *channels_count)
795 {
796   SilcIDCacheList list = NULL;
797   SilcIDCacheEntry id_cache = NULL;
798   SilcChannelEntry *channels = NULL;
799   int i = 0;
800
801   SILC_LOG_DEBUG(("Start"));
802
803   if (!channel_id) {
804     if (!silc_idcache_get_all(id_list->channels, &list))
805       return NULL;
806
807     channels = silc_calloc(silc_idcache_list_count(list), sizeof(*channels));
808     
809     i = 0;
810     silc_idcache_list_first(list, &id_cache);
811     channels[i++] = (SilcChannelEntry)id_cache->context;
812     
813     while (silc_idcache_list_next(list, &id_cache))
814       channels[i++] = (SilcChannelEntry)id_cache->context;
815     
816     silc_idcache_list_free(list);
817   } else {
818     if (!silc_idcache_find_by_id_one(id_list->channels, channel_id, &id_cache))
819       return NULL;
820
821     i = 1;
822     channels = silc_calloc(1, sizeof(*channels));
823     channels[0] = (SilcChannelEntry)id_cache->context;
824   }
825
826   if (channels_count)
827     *channels_count = i;
828
829   return channels;
830 }