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