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