Added preliminary support for non-backup-aware servers in cell
[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(("Start"));
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     /* Free data */
372     silc_free(entry->nickname);
373     silc_free(entry->servername);
374     silc_free(entry->username);
375     silc_free(entry->userinfo);
376     silc_free(entry->id);
377     silc_hash_table_free(entry->channels);
378
379     memset(entry, 'F', sizeof(*entry));
380     silc_free(entry);
381
382     return TRUE;
383   }
384
385   return FALSE;
386 }
387
388 /* Returns all clients matching requested nickname. Number of clients is
389    returned to `clients_count'. Caller must free the returned table. */
390
391 int silc_idlist_get_clients_by_nickname(SilcIDList id_list, char *nickname,
392                                         char *server, 
393                                         SilcClientEntry **clients,
394                                         SilcUInt32 *clients_count)
395 {
396   SilcIDCacheList list = NULL;
397   SilcIDCacheEntry id_cache = NULL;
398
399   SILC_LOG_DEBUG(("Start"));
400
401   if (!silc_idcache_find_by_name(id_list->clients, nickname, &list))
402     return FALSE;
403
404   *clients = silc_realloc(*clients, 
405                           (silc_idcache_list_count(list) + *clients_count) * 
406                           sizeof(**clients));
407
408   silc_idcache_list_first(list, &id_cache);
409   (*clients)[(*clients_count)++] = (SilcClientEntry)id_cache->context;
410
411   while (silc_idcache_list_next(list, &id_cache))
412     (*clients)[(*clients_count)++] = (SilcClientEntry)id_cache->context;
413   
414   silc_idcache_list_free(list);
415   
416   SILC_LOG_DEBUG(("Found total %d clients", *clients_count));
417
418   return TRUE;
419 }
420
421 /* Returns all clients matching requested nickname hash. Number of clients
422    is returned to `clients_count'. Caller must free the returned table. */
423
424 int silc_idlist_get_clients_by_hash(SilcIDList id_list, char *nickname,
425                                     SilcHash md5hash,
426                                     SilcClientEntry **clients,
427                                     SilcUInt32 *clients_count)
428 {
429   SilcIDCacheList list = NULL;
430   SilcIDCacheEntry id_cache = NULL;
431   unsigned char hash[32];
432   SilcClientID client_id;
433   char nick[128 + 1];
434
435   SILC_LOG_DEBUG(("Start"));
436
437   memset(nick, 0, sizeof(nick));
438   silc_to_lower(nickname, nick, sizeof(nick) - 1);
439   silc_hash_make(md5hash, nick, strlen(nick), hash);
440
441   /* As the Client ID is hashed in the ID cache by hashing only the hash
442      from the Client ID, we can do a lookup with only the hash not the
443      other parts of the ID and get all the clients with that hash, ie.
444      with that nickname, as the hash is from the nickname. */
445   memset(&client_id, 0, sizeof(client_id));
446   memcpy(&client_id.hash, hash, sizeof(client_id.hash));
447   if (!silc_idcache_find_by_id(id_list->clients, &client_id, &list))
448     return FALSE;
449
450   *clients = silc_realloc(*clients, 
451                           (silc_idcache_list_count(list) + *clients_count) * 
452                           sizeof(**clients));
453
454   silc_idcache_list_first(list, &id_cache);
455   (*clients)[(*clients_count)++] = (SilcClientEntry)id_cache->context;
456
457   while (silc_idcache_list_next(list, &id_cache))
458     (*clients)[(*clients_count)++] = (SilcClientEntry)id_cache->context;
459   
460   silc_idcache_list_free(list);
461   
462   SILC_LOG_DEBUG(("Found total %d clients", *clients_count));
463
464   return TRUE;
465 }
466
467 /* Finds client by Client ID */
468
469 SilcClientEntry
470 silc_idlist_find_client_by_id(SilcIDList id_list, SilcClientID *id,
471                               bool registered, SilcIDCacheEntry *ret_entry)
472 {
473   SilcIDCacheEntry id_cache = NULL;
474   SilcClientEntry client;
475
476   if (!id)
477     return NULL;
478
479   SILC_LOG_DEBUG(("Client ID (%s)", 
480                   silc_id_render(id, SILC_ID_CLIENT)));
481
482   /* Do extended search since the normal ID comparison function for
483      Client ID's compares only the hash from the Client ID and not the
484      entire ID. The silc_hash_client_id_compare compares the entire
485      Client ID as we want to find one specific Client ID. */
486   if (!silc_idcache_find_by_id_one_ext(id_list->clients, (void *)id, 
487                                        NULL, NULL, 
488                                        silc_hash_client_id_compare, NULL,
489                                        &id_cache))
490     return NULL;
491
492   client = (SilcClientEntry)id_cache->context;
493
494   if (ret_entry)
495     *ret_entry = id_cache;
496
497   if (client && registered &&
498       !(client->data.status & SILC_IDLIST_STATUS_REGISTERED))
499     return NULL;
500
501   SILC_LOG_DEBUG(("Found"));
502
503   return client;
504 }
505
506 /* Replaces old Client ID with new one */
507
508 SilcClientEntry
509 silc_idlist_replace_client_id(SilcServer server,
510                               SilcIDList id_list, SilcClientID *old_id,
511                               SilcClientID *new_id, const char *nickname)
512 {
513   SilcIDCacheEntry id_cache = NULL;
514   SilcClientEntry client;
515
516   if (!old_id || !new_id)
517     return NULL;
518
519   SILC_LOG_DEBUG(("Replacing Client ID"));
520
521   /* Do extended search since the normal ID comparison function for
522      Client ID's compares only the hash from the Client ID and not the
523      entire ID. The silc_hash_client_id_compare compares the entire
524      Client ID as we want to find one specific Client ID. */
525   if (!silc_idcache_find_by_id_one_ext(id_list->clients, (void *)old_id, 
526                                        NULL, NULL, 
527                                        silc_hash_client_id_compare, NULL,
528                                        &id_cache))
529     return NULL;
530
531   client = (SilcClientEntry)id_cache->context;
532
533   /* Remove the old entry and add a new one */
534
535   if (!silc_idcache_del_by_context(id_list->clients, client))
536     return NULL;
537
538   /* Check if anyone is watching old nickname */
539   if (server->server_type == SILC_ROUTER)
540     silc_server_check_watcher_list(server, client, nickname,
541                                    SILC_NOTIFY_TYPE_NICK_CHANGE);
542
543   silc_free(client->id);
544   silc_free(client->nickname);
545   client->id = new_id;
546   client->nickname = nickname ? strdup(nickname) : NULL;
547
548   /* Check if anyone is watching new nickname */
549   if (server->server_type == SILC_ROUTER)
550     silc_server_check_watcher_list(server, client, nickname,
551                                    SILC_NOTIFY_TYPE_NICK_CHANGE);
552
553   if (!silc_idcache_add(id_list->clients, client->nickname, client->id, 
554                         client, 0, NULL))
555     return NULL;
556
557   SILC_LOG_DEBUG(("Replaced"));
558
559   return client;
560 }
561
562 /* Client cache entry destructor that is called when the cache is purged. */
563
564 void silc_idlist_client_destructor(SilcIDCache cache,
565                                    SilcIDCacheEntry entry)
566 {
567   SilcClientEntry client;
568
569   client = (SilcClientEntry)entry->context;
570   if (client) {
571     silc_free(client->nickname);
572     silc_free(client->username);
573     silc_free(client->userinfo);
574     silc_free(client->id);
575     silc_hash_table_free(client->channels);
576
577     memset(client, 'A', sizeof(*client));
578     silc_free(client);
579   }
580 }
581
582 /******************************************************************************
583
584                           Channel entry functions
585
586 ******************************************************************************/
587
588 /* Add new channel entry. This add the new channel entry to the ID cache
589    system and returns the allocated entry or NULL on error. */
590
591 SilcChannelEntry
592 silc_idlist_add_channel(SilcIDList id_list, char *channel_name, int mode,
593                         SilcChannelID *id, SilcServerEntry router,
594                         SilcCipher channel_key, SilcHmac hmac,
595                         int expire)
596 {
597   SilcChannelEntry channel;
598
599   SILC_LOG_DEBUG(("Adding new channel %s", channel_name));
600
601   channel = silc_calloc(1, sizeof(*channel));
602   channel->channel_name = channel_name;
603   channel->mode = mode;
604   channel->id = id;
605   channel->router = router;
606   channel->channel_key = channel_key;
607   channel->hmac = hmac;
608   channel->created = time(0);
609   if (!channel->hmac)
610     if (!silc_hmac_alloc(SILC_DEFAULT_HMAC, NULL, &channel->hmac)) {
611       silc_free(channel);
612       return NULL;
613     }
614
615   channel->user_list = silc_hash_table_alloc(3, silc_hash_ptr, NULL, NULL,
616                                              NULL, NULL, NULL, TRUE);
617
618   if (!silc_idcache_add(id_list->channels, channel->channel_name, 
619                         (void *)channel->id, (void *)channel, expire, NULL)) {
620     silc_hmac_free(channel->hmac);
621     silc_hash_table_free(channel->user_list);
622     silc_free(channel);
623     return NULL;
624   }
625
626   return channel;
627 }
628
629 /* Foreach callbcak to free all users from the channel when deleting a
630    channel entry. */
631
632 static void silc_idlist_del_channel_foreach(void *key, void *context,
633                                             void *user_context)
634 {
635   SilcChannelClientEntry chl = (SilcChannelClientEntry)context;
636
637   SILC_LOG_DEBUG(("Removing client %s from channel %s",
638                   chl->client->nickname, chl->channel->channel_name));
639
640   /* Remove the context from the client's channel hash table as that
641      table and channel's user_list hash table share this same context. */
642   silc_hash_table_del(chl->client->channels, chl->channel);
643   silc_free(chl);
644 }
645
646 /* Free channel entry.  This free's everything. */
647
648 int silc_idlist_del_channel(SilcIDList id_list, SilcChannelEntry entry)
649 {
650   if (entry) {
651     SILC_LOG_DEBUG(("Deleting channel %s", entry->channel_name));
652
653     /* Remove from cache */
654     if (!silc_idcache_del_by_context(id_list->channels, entry))
655       return FALSE;
656
657     /* Free all client entrys from the users list. The silc_hash_table_free
658        will free all the entries so they are not freed at the foreach 
659        callback. */
660     silc_hash_table_foreach(entry->user_list, silc_idlist_del_channel_foreach,
661                             NULL);
662     silc_hash_table_free(entry->user_list);
663
664     /* Free data */
665     silc_free(entry->channel_name);
666     silc_free(entry->id);
667     silc_free(entry->topic);
668     if (entry->channel_key)
669       silc_cipher_free(entry->channel_key);
670     if (entry->key) {
671       memset(entry->key, 0, entry->key_len / 8);
672       silc_free(entry->key);
673     }
674     silc_free(entry->cipher);
675     if (entry->hmac)
676       silc_hmac_free(entry->hmac);
677     silc_free(entry->hmac_name);
678     silc_free(entry->rekey);
679     if (entry->founder_key)
680       silc_pkcs_public_key_free(entry->founder_key);
681
682     memset(entry, 'F', sizeof(*entry));
683     silc_free(entry);
684     return TRUE;
685   }
686
687   return FALSE;
688 }
689
690 /* Finds channel by channel name. Channel names are unique and they
691    are not case-sensitive. */
692
693 SilcChannelEntry
694 silc_idlist_find_channel_by_name(SilcIDList id_list, char *name,
695                                  SilcIDCacheEntry *ret_entry)
696 {
697   SilcIDCacheEntry id_cache = NULL;
698
699   SILC_LOG_DEBUG(("Channel by name %s", name));
700
701   if (!silc_idcache_find_by_name_one(id_list->channels, name, &id_cache))
702     return NULL;
703
704   if (ret_entry)
705     *ret_entry = id_cache;
706
707   SILC_LOG_DEBUG(("Found"));
708
709   return id_cache->context;
710 }
711
712 /* Finds channel by Channel ID. */
713
714 SilcChannelEntry
715 silc_idlist_find_channel_by_id(SilcIDList id_list, SilcChannelID *id,
716                                SilcIDCacheEntry *ret_entry)
717 {
718   SilcIDCacheEntry id_cache = NULL;
719   SilcChannelEntry channel;
720
721   if (!id)
722     return NULL;
723
724   SILC_LOG_DEBUG(("Channel ID (%s)",
725                   silc_id_render(id, SILC_ID_CHANNEL)));
726
727   if (!silc_idcache_find_by_id_one(id_list->channels, (void *)id, &id_cache))
728     return NULL;
729
730   channel = (SilcChannelEntry)id_cache->context;
731
732   if (ret_entry)
733     *ret_entry = id_cache;
734
735   SILC_LOG_DEBUG(("Found"));
736
737   return channel;
738 }
739
740 /* Replaces old Channel ID with new one. This is done when router forces
741    normal server to change Channel ID. */
742
743 SilcChannelEntry
744 silc_idlist_replace_channel_id(SilcIDList id_list, SilcChannelID *old_id,
745                                SilcChannelID *new_id)
746 {
747   SilcIDCacheEntry id_cache = NULL;
748   SilcChannelEntry channel;
749
750   if (!old_id || !new_id)
751     return NULL;
752
753   SILC_LOG_DEBUG(("Replacing Channel ID"));
754
755   if (!silc_idcache_find_by_id_one(id_list->channels, (void *)old_id, 
756                                    &id_cache))
757     return NULL;
758
759   channel = (SilcChannelEntry)id_cache->context;
760
761   /* Remove the old entry and add a new one */
762
763   silc_idcache_del_by_id(id_list->channels, (void *)channel->id);
764
765   silc_free(channel->id);
766   channel->id = new_id;
767
768   silc_idcache_add(id_list->channels, channel->channel_name, channel->id, 
769                    channel, 0, NULL);
770
771   SILC_LOG_DEBUG(("Replaced"));
772
773   return channel;
774 }
775
776 /* Returns channels from the ID list. If the `channel_id' is NULL then
777    all channels are returned. */
778
779 SilcChannelEntry *
780 silc_idlist_get_channels(SilcIDList id_list, SilcChannelID *channel_id,
781                          SilcUInt32 *channels_count)
782 {
783   SilcIDCacheList list = NULL;
784   SilcIDCacheEntry id_cache = NULL;
785   SilcChannelEntry *channels = NULL;
786   int i = 0;
787
788   SILC_LOG_DEBUG(("Start"));
789
790   if (!channel_id) {
791     if (!silc_idcache_get_all(id_list->channels, &list))
792       return NULL;
793
794     channels = silc_calloc(silc_idcache_list_count(list), sizeof(*channels));
795     
796     i = 0;
797     silc_idcache_list_first(list, &id_cache);
798     channels[i++] = (SilcChannelEntry)id_cache->context;
799     
800     while (silc_idcache_list_next(list, &id_cache))
801       channels[i++] = (SilcChannelEntry)id_cache->context;
802     
803     silc_idcache_list_free(list);
804   } else {
805     if (!silc_idcache_find_by_id_one(id_list->channels, channel_id, &id_cache))
806       return NULL;
807
808     i = 1;
809     channels = silc_calloc(1, sizeof(*channels));
810     channels[0] = (SilcChannelEntry)id_cache->context;
811   }
812
813   if (channels_count)
814     *channels_count = i;
815
816   return channels;
817 }