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