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