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