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