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->rekey = idata->rekey;
40   data->hash = idata->hash;
41   data->hmac_send = idata->hmac_send;
42   data->hmac_receive = idata->hmac_receive;
43   data->public_key = idata->public_key;
44   data->last_receive = idata->last_receive;
45   data->last_sent = idata->last_sent;
46   data->registered = idata->registered;
47 }
48
49 /* Free's all data in the common ID entry data structure. */
50
51 void silc_idlist_del_data(void *entry)
52 {
53   SilcIDListData idata = (SilcIDListData)entry;
54   if (idata->send_key)
55     silc_cipher_free(idata->send_key);
56   if (idata->receive_key)
57     silc_cipher_free(idata->receive_key);
58   if (idata->rekey) {
59     if (idata->rekey->send_enc_key) {
60       memset(idata->rekey->send_enc_key, 0, idata->rekey->enc_key_len);
61       silc_free(idata->rekey->send_enc_key);
62     }
63     silc_free(idata->rekey);
64   }
65   if (idata->hmac_send)         /* Same as idata->hmac_receive */
66     silc_hmac_free(idata->hmac_send);
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 int silc_idlist_del_server(SilcIDList id_list, SilcServerEntry entry)
255 {
256   SILC_LOG_DEBUG(("Start"));
257
258   if (entry) {
259     /* Remove from cache */
260     if (entry->id)
261       if (!silc_idcache_del_by_id(id_list->servers, SILC_ID_SERVER, 
262                                   (void *)entry->id))
263         return FALSE;
264
265     /* Free data */
266     if (entry->server_name)
267       silc_free(entry->server_name);
268     if (entry->id)
269       silc_free(entry->id);
270
271     memset(entry, 'F', sizeof(*entry));
272     silc_free(entry);
273     return TRUE;
274   }
275
276   return FALSE;
277 }
278
279 /******************************************************************************
280
281                           Client entry functions
282
283 ******************************************************************************/
284
285 /* Add new client entry. This adds the client entry to ID cache system
286    and returns the allocated client entry or NULL on error.  This is
287    called when new client connection is accepted to the server. If The
288    `router' is provided then the all server routines assume that the client
289    is not directly connected local client but it has router set and is
290    remote.  If this is the case then `connection' must be NULL.  If, on the
291    other hand, the `connection' is provided then the client is assumed
292    to be directly connected local client and `router' must be NULL. */
293
294 SilcClientEntry
295 silc_idlist_add_client(SilcIDList id_list, unsigned char *nickname, 
296                        uint32 nickname_len, char *username, 
297                        char *userinfo, SilcClientID *id, 
298                        SilcServerEntry router, void *connection)
299 {
300   SilcClientEntry client;
301
302   SILC_LOG_DEBUG(("Adding new client entry"));
303
304   client = silc_calloc(1, sizeof(*client));
305   client->nickname = nickname;
306   client->username = username;
307   client->userinfo = userinfo;
308   client->id = id;
309   client->router = router;
310   client->connection = connection;
311   silc_list_init(client->channels, struct SilcChannelClientEntryStruct, 
312                  client_list);
313
314   if (!silc_idcache_add(id_list->clients, nickname,  nickname_len,
315                         SILC_ID_CLIENT, (void *)client->id, 
316                         (void *)client, TRUE, FALSE)) {
317     silc_free(client);
318     return NULL;
319   }
320
321   return client;
322 }
323
324 /* Free client entry. This free's everything and removes the entry
325    from ID cache. Call silc_idlist_del_data before calling this one. */
326
327 int silc_idlist_del_client(SilcIDList id_list, SilcClientEntry entry)
328 {
329   SILC_LOG_DEBUG(("Start"));
330
331   if (entry) {
332     /* Remove from cache */
333     if (entry->id)
334       if (!silc_idcache_del_by_id(id_list->clients, SILC_ID_CLIENT, 
335                                   (void *)entry->id))
336         return FALSE;
337
338     /* Free data */
339     if (entry->nickname)
340       silc_free(entry->nickname);
341     if (entry->username)
342       silc_free(entry->username);
343     if (entry->userinfo)
344       silc_free(entry->userinfo);
345     if (entry->id)
346       silc_free(entry->id);
347
348     memset(entry, 'F', sizeof(*entry));
349     silc_free(entry);
350
351     return TRUE;
352   }
353
354   return FALSE;
355 }
356
357 /* Returns all clients matching requested nickname. Number of clients is
358    returned to `clients_count'. Caller must free the returned table. */
359
360 int silc_idlist_get_clients_by_nickname(SilcIDList id_list, char *nickname,
361                                         char *server, 
362                                         SilcClientEntry **clients,
363                                         uint32 *clients_count)
364 {
365   SilcIDCacheList list = NULL;
366   SilcIDCacheEntry id_cache = NULL;
367   int i;
368
369   SILC_LOG_DEBUG(("Start"));
370
371   if (!silc_idcache_find_by_data(id_list->clients, nickname, &list))
372     return FALSE;
373
374   *clients = silc_realloc(*clients, 
375                           (silc_idcache_list_count(list) + *clients_count) * 
376                           sizeof(**clients));
377
378   i = 0;
379   silc_idcache_list_first(list, &id_cache);
380   (*clients)[i++] = (SilcClientEntry)id_cache->context;
381
382   while (silc_idcache_list_next(list, &id_cache))
383     (*clients)[i++] = (SilcClientEntry)id_cache->context;
384   
385   silc_idcache_list_free(list);
386   
387   *clients_count += i;
388
389   return TRUE;
390 }
391
392 /* Returns all clients matching requested nickname. Number of clients is
393    returned to `clients_count'. Caller must free the returned table. */
394 /* XXX This actually checks the data, which can be hash of the nickname
395    but is not if the client is local client. Global client on global
396    list may have hash.  Thus, this is not fully reliable function.
397    Instead this should probably check the hash from the list of client ID's. */
398
399 int silc_idlist_get_clients_by_hash(SilcIDList id_list, char *nickname,
400                                     SilcHash md5hash,
401                                     SilcClientEntry **clients,
402                                     uint32 *clients_count)
403 {
404   SilcIDCacheList list = NULL;
405   SilcIDCacheEntry id_cache = NULL;
406   unsigned char hash[32];
407   int i;
408
409   SILC_LOG_DEBUG(("Start"));
410
411   silc_hash_make(md5hash, nickname, strlen(nickname), hash);
412
413   if (!silc_idcache_find_by_data(id_list->clients, hash, &list))
414     return FALSE;
415
416   *clients = silc_realloc(*clients, 
417                           (silc_idcache_list_count(list) + *clients_count) * 
418                           sizeof(**clients));
419
420   i = 0;
421   silc_idcache_list_first(list, &id_cache);
422   (*clients)[i++] = (SilcClientEntry)id_cache->context;
423
424   while (silc_idcache_list_next(list, &id_cache))
425     (*clients)[i++] = (SilcClientEntry)id_cache->context;
426   
427   silc_idcache_list_free(list);
428   
429   *clients_count += i;
430
431   return TRUE;
432 }
433
434 /* Finds client by nickname hash. */
435
436 SilcClientEntry
437 silc_idlist_find_client_by_hash(SilcIDList id_list, char *nickname,
438                                 SilcHash md5hash, SilcIDCacheEntry *ret_entry)
439 {
440   SilcIDCacheList list = NULL;
441   SilcIDCacheEntry id_cache = NULL;
442   SilcClientEntry client = NULL;
443   unsigned char hash[32];
444
445   SILC_LOG_DEBUG(("Client by hash"));
446
447   silc_hash_make(md5hash, nickname, strlen(nickname), hash);
448
449   if (!silc_idcache_find_by_id(id_list->clients, SILC_ID_CACHE_ANY, 
450                                SILC_ID_CLIENT, &list))
451     return NULL;
452
453   if (!silc_idcache_list_first(list, &id_cache)) {
454     silc_idcache_list_free(list);
455     return NULL;
456   }
457
458   while (id_cache) {
459     client = (SilcClientEntry)id_cache->context;
460     
461     if (client && !SILC_ID_COMPARE_HASH(client->id, hash))
462       break;
463
464     id_cache = NULL;
465     client = NULL;
466
467     if (!silc_idcache_list_next(list, &id_cache))
468       break;
469   }
470   
471   silc_idcache_list_free(list);
472
473   if (ret_entry)
474     *ret_entry = id_cache;
475
476   SILC_LOG_DEBUG(("Found"));
477
478   return client;
479 }
480
481 /* Finds client by Client ID */
482
483 SilcClientEntry
484 silc_idlist_find_client_by_id(SilcIDList id_list, SilcClientID *id,
485                               SilcIDCacheEntry *ret_entry)
486 {
487   SilcIDCacheEntry id_cache = NULL;
488   SilcClientEntry client;
489
490   if (!id)
491     return NULL;
492
493   SILC_LOG_DEBUG(("Client ID (%s)", 
494                   silc_id_render(id, SILC_ID_CLIENT)));
495
496   if (!silc_idcache_find_by_id_one(id_list->clients, (void *)id, 
497                                    SILC_ID_CLIENT, &id_cache))
498     return NULL;
499
500   client = (SilcClientEntry)id_cache->context;
501
502   if (ret_entry)
503     *ret_entry = id_cache;
504
505   SILC_LOG_DEBUG(("Found"));
506
507   return client;
508 }
509
510 /* Replaces old Client ID with new one */
511
512 SilcClientEntry
513 silc_idlist_replace_client_id(SilcIDList id_list, SilcClientID *old_id,
514                               SilcClientID *new_id)
515 {
516   SilcIDCacheEntry id_cache = NULL;
517   SilcClientEntry client;
518
519   if (!old_id || !new_id)
520     return NULL;
521
522   SILC_LOG_DEBUG(("Replacing Client ID"));
523
524   if (!silc_idcache_find_by_id_one(id_list->clients, (void *)old_id, 
525                                    SILC_ID_CLIENT, &id_cache))
526     return NULL;
527
528   client = (SilcClientEntry)id_cache->context;
529   silc_free(client->id);
530   client->id = new_id;
531   id_cache->id = (void *)new_id;
532
533   /* If the old ID Cache data was the hash value of the old Client ID
534      replace it with the hash of new Client ID */
535   if (id_cache->data && !SILC_ID_COMPARE_HASH(old_id, id_cache->data)) {
536     silc_free(id_cache->data);
537     id_cache->data = silc_calloc(sizeof(new_id->hash), sizeof(unsigned char));
538     memcpy(id_cache->data, new_id->hash, sizeof(new_id->hash));
539     silc_idcache_sort_by_data(id_list->clients);
540   }
541
542   SILC_LOG_DEBUG(("Replaced"));
543
544   return client;
545 }
546
547 /* Client cache entry destructor that is called when the cache is purged. */
548
549 void silc_idlist_client_destructor(SilcIDCache cache,
550                                    SilcIDCacheEntry entry)
551 {
552   SilcClientEntry client;
553
554   SILC_LOG_DEBUG(("Start"));
555
556   client = (SilcClientEntry)entry->context;
557   if (client) {
558     if (client->nickname)
559       silc_free(client->nickname);
560     if (client->username)
561       silc_free(client->username);
562     if (client->userinfo)
563       silc_free(client->userinfo);
564     if (client->id)
565       silc_free(client->id);
566
567     memset(client, 'F', sizeof(*client));
568     silc_free(client);
569   }
570 }
571
572 /******************************************************************************
573
574                           Channel entry functions
575
576 ******************************************************************************/
577
578 /* Add new channel entry. This add the new channel entry to the ID cache
579    system and returns the allocated entry or NULL on error. */
580
581 SilcChannelEntry
582 silc_idlist_add_channel(SilcIDList id_list, char *channel_name, int mode,
583                         SilcChannelID *id, SilcServerEntry router,
584                         SilcCipher channel_key, SilcHmac hmac)
585 {
586   SilcChannelEntry channel;
587
588   channel = silc_calloc(1, sizeof(*channel));
589   channel->channel_name = channel_name;
590   channel->mode = mode;
591   channel->id = id;
592   channel->router = router;
593   channel->channel_key = channel_key;
594   channel->hmac = hmac;
595   if (!channel->hmac)
596     if (!silc_hmac_alloc("hmac-sha1-96", NULL, &channel->hmac)) {
597       silc_free(channel);
598       return NULL;
599     }
600
601   silc_list_init(channel->user_list, struct SilcChannelClientEntryStruct, 
602                  channel_list);
603
604   if (!silc_idcache_add(id_list->channels, channel->channel_name, 
605                         channel->channel_name ? strlen(channel->channel_name) :
606                         0, SILC_ID_CHANNEL, 
607                         (void *)channel->id, (void *)channel, TRUE, FALSE)) {
608     silc_free(channel);
609     return NULL;
610   }
611
612   return channel;
613 }
614
615 /* Free channel entry.  This free's everything. */
616
617 int silc_idlist_del_channel(SilcIDList id_list, SilcChannelEntry entry)
618 {
619   SILC_LOG_DEBUG(("Start"));
620
621   if (entry) {
622     SilcChannelClientEntry chl;
623
624     /* Remove from cache */
625     if (entry->id)
626       if (!silc_idcache_del_by_id(id_list->channels, SILC_ID_CHANNEL, 
627                                   (void *)entry->id))
628         return FALSE;
629
630     /* Free data */
631     if (entry->channel_name)
632       silc_free(entry->channel_name);
633     if (entry->id)
634       silc_free(entry->id);
635     if (entry->topic)
636       silc_free(entry->topic);
637     if (entry->channel_key)
638       silc_cipher_free(entry->channel_key);
639     if (entry->key) {
640       memset(entry->key, 0, entry->key_len / 8);
641       silc_free(entry->key);
642     }
643     if (entry->cipher)
644       silc_free(entry->cipher);
645     if (entry->hmac_name)
646       silc_free(entry->hmac_name);
647     if (entry->rekey)
648       silc_free(entry->rekey);
649
650     /* Free all data, free also any reference from the client's channel
651        list since they share the same memory. */
652     silc_list_start(entry->user_list);
653     while ((chl = silc_list_get(entry->user_list)) != SILC_LIST_END) {
654       silc_list_del(chl->client->channels, chl);
655       silc_list_del(entry->user_list, chl);
656       silc_free(chl);
657     }
658
659     memset(entry, 'F', sizeof(*entry));
660     silc_free(entry);
661     return TRUE;
662   }
663
664   return FALSE;
665 }
666
667 /* Finds channel by channel name. Channel names are unique and they
668    are not case-sensitive. */
669
670 SilcChannelEntry
671 silc_idlist_find_channel_by_name(SilcIDList id_list, char *name,
672                                  SilcIDCacheEntry *ret_entry)
673 {
674   SilcIDCacheList list = NULL;
675   SilcIDCacheEntry id_cache = NULL;
676   SilcChannelEntry channel;
677
678   SILC_LOG_DEBUG(("Channel by name"));
679
680   if (!silc_idcache_find_by_data_loose(id_list->channels, name, &list))
681     return NULL;
682   
683   if (!silc_idcache_list_first(list, &id_cache)) {
684     silc_idcache_list_free(list);
685     return NULL;
686   }
687
688   channel = (SilcChannelEntry)id_cache->context;
689
690   if (ret_entry)
691     *ret_entry = id_cache;
692
693   silc_idcache_list_free(list);
694
695   SILC_LOG_DEBUG(("Found"));
696
697   return channel;
698 }
699
700 /* Finds channel by Channel ID. */
701
702 SilcChannelEntry
703 silc_idlist_find_channel_by_id(SilcIDList id_list, SilcChannelID *id,
704                                SilcIDCacheEntry *ret_entry)
705 {
706   SilcIDCacheEntry id_cache = NULL;
707   SilcChannelEntry channel;
708
709   if (!id)
710     return NULL;
711
712   SILC_LOG_DEBUG(("Channel ID (%s)",
713                   silc_id_render(id, SILC_ID_CHANNEL)));
714
715   if (!silc_idcache_find_by_id_one(id_list->channels, (void *)id, 
716                                    SILC_ID_CHANNEL, &id_cache))
717     return NULL;
718
719   channel = (SilcChannelEntry)id_cache->context;
720
721   if (ret_entry)
722     *ret_entry = id_cache;
723
724   SILC_LOG_DEBUG(("Found"));
725
726   return channel;
727 }
728
729 /* Replaces old Channel ID with new one. This is done when router forces
730    normal server to change Channel ID. */
731
732 SilcChannelEntry
733 silc_idlist_replace_channel_id(SilcIDList id_list, SilcChannelID *old_id,
734                                SilcChannelID *new_id)
735 {
736   SilcIDCacheEntry id_cache = NULL;
737   SilcChannelEntry channel;
738
739   if (!old_id || !new_id)
740     return NULL;
741
742   SILC_LOG_DEBUG(("Replacing Channel ID"));
743
744   if (!silc_idcache_find_by_id_one(id_list->channels, (void *)old_id, 
745                                    SILC_ID_CHANNEL, &id_cache))
746     return NULL;
747
748   channel = (SilcChannelEntry)id_cache->context;
749   silc_free(channel->id);
750   channel->id = new_id;
751   id_cache->id = (void *)new_id;
752
753   SILC_LOG_DEBUG(("Replaced"));
754
755   return channel;
756 }
757
758 /* Returns channels from the ID list. If the `channel_id' is NULL then
759    all channels are returned. */
760
761 SilcChannelEntry *
762 silc_idlist_get_channels(SilcIDList id_list, SilcChannelID *channel_id,
763                          uint32 *channels_count)
764 {
765   SilcIDCacheList list = NULL;
766   SilcIDCacheEntry id_cache = NULL;
767   SilcChannelEntry *channels;
768   int i;
769
770   SILC_LOG_DEBUG(("Start"));
771
772   if (!silc_idcache_find_by_id(id_list->channels, channel_id ? channel_id :
773                                SILC_ID_CACHE_ANY, SILC_ID_CHANNEL, &list))
774     return NULL;
775
776   channels = silc_calloc(silc_idcache_list_count(list), sizeof(*channels));
777
778   i = 0;
779   silc_idcache_list_first(list, &id_cache);
780   channels[i++] = (SilcChannelEntry)id_cache->context;
781
782   while (silc_idcache_list_next(list, &id_cache))
783     channels[i++] = (SilcChannelEntry)id_cache->context;
784   
785   silc_idcache_list_free(list);
786   
787   if (channels_count)
788     *channels_count = i;
789
790   return channels;
791 }