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->status = idata->status;
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 && 
148       !(server->data.status & SILC_IDLIST_STATUS_REGISTERED))
149     return NULL;
150
151   SILC_LOG_DEBUG(("Found"));
152
153   return server;
154 }
155
156 /* Find server by name */
157
158 SilcServerEntry
159 silc_idlist_find_server_by_name(SilcIDList id_list, char *name,
160                                 bool registered, SilcIDCacheEntry *ret_entry)
161 {
162   SilcIDCacheEntry id_cache = NULL;
163   SilcServerEntry server;
164
165   SILC_LOG_DEBUG(("Server by name `%s'", name));
166
167   if (!silc_idcache_find_by_name_one(id_list->servers, name, &id_cache))
168     return NULL;
169
170   server = (SilcServerEntry)id_cache->context;
171   
172   if (ret_entry)
173     *ret_entry = id_cache;
174
175   if (server && registered &&
176       !(server->data.status & SILC_IDLIST_STATUS_REGISTERED))
177     return NULL;
178
179   SILC_LOG_DEBUG(("Found"));
180
181   return server;
182 }
183
184 /* Find server by connection parameters, hostname and port */
185
186 SilcServerEntry
187 silc_idlist_find_server_by_conn(SilcIDList id_list, char *hostname,
188                                 int port, bool registered,
189                                 SilcIDCacheEntry *ret_entry)
190 {
191   SilcIDCacheList list = NULL;
192   SilcIDCacheEntry id_cache = NULL;
193   SilcServerEntry server = NULL;
194   SilcSocketConnection sock;
195  
196   SILC_LOG_DEBUG(("Server by hostname %s and port %d", hostname, port));
197
198   if (!silc_idcache_get_all(id_list->servers, &list))
199     return NULL;
200
201   if (!silc_idcache_list_first(list, &id_cache)) {
202     silc_idcache_list_free(list);
203     return NULL;
204   }
205
206   while (id_cache) {
207     server = (SilcServerEntry)id_cache->context;
208     sock = (SilcSocketConnection)server->connection;
209     
210     if (sock && ((sock->hostname && !strcasecmp(sock->hostname, hostname)) ||
211                  (sock->ip && !strcasecmp(sock->ip, hostname)))
212         && sock->port == port)
213       break;
214
215     id_cache = NULL;
216     server = NULL;
217
218     if (!silc_idcache_list_next(list, &id_cache))
219       break;
220   }
221   
222   silc_idcache_list_free(list);
223
224   if (ret_entry)
225     *ret_entry = id_cache;
226
227   if (server && registered &&
228       !(server->data.status & SILC_IDLIST_STATUS_REGISTERED))
229     return NULL;
230
231   SILC_LOG_DEBUG(("Found"));
232
233   return server;
234 }
235
236 /* Replaces old Server ID with new one */ 
237
238 SilcServerEntry
239 silc_idlist_replace_server_id(SilcIDList id_list, SilcServerID *old_id,
240                               SilcServerID *new_id)
241 {
242   SilcIDCacheEntry id_cache = NULL;
243   SilcServerEntry server;
244
245   if (!old_id || !new_id)
246     return NULL;
247
248   SILC_LOG_DEBUG(("Replacing Server ID"));
249
250   if (!silc_idcache_find_by_id_one(id_list->servers, (void *)old_id, 
251                                    &id_cache))
252     return NULL;
253
254   server = (SilcServerEntry)id_cache->context;
255
256   /* Remove the old entry and add a new one */
257
258   silc_idcache_del_by_id(id_list->servers, (void *)server->id);
259
260   silc_free(server->id);
261   server->id = new_id;
262
263   silc_idcache_add(id_list->servers, server->server_name, server->id, 
264                    server, FALSE);
265
266   SILC_LOG_DEBUG(("Found"));
267
268   return server;
269 }
270
271 /* Removes and free's server entry from ID list */
272
273 int silc_idlist_del_server(SilcIDList id_list, SilcServerEntry entry)
274 {
275   SILC_LOG_DEBUG(("Start"));
276
277   if (entry) {
278     /* Remove from cache */
279     if (entry->id)
280       if (!silc_idcache_del_by_id(id_list->servers, (void *)entry->id))
281         return FALSE;
282
283     /* Free data */
284     if (entry->server_name)
285       silc_free(entry->server_name);
286     if (entry->id)
287       silc_free(entry->id);
288
289     memset(entry, 'F', sizeof(*entry));
290     silc_free(entry);
291     return TRUE;
292   }
293
294   return FALSE;
295 }
296
297 /******************************************************************************
298
299                           Client entry functions
300
301 ******************************************************************************/
302
303 /* Add new client entry. This adds the client entry to ID cache system
304    and returns the allocated client entry or NULL on error.  This is
305    called when new client connection is accepted to the server. If The
306    `router' is provided then the all server routines assume that the client
307    is not directly connected local client but it has router set and is
308    remote.  If this is the case then `connection' must be NULL.  If, on the
309    other hand, the `connection' is provided then the client is assumed
310    to be directly connected local client and `router' must be NULL. */
311
312 SilcClientEntry
313 silc_idlist_add_client(SilcIDList id_list, char *nickname, char *username, 
314                        char *userinfo, SilcClientID *id, 
315                        SilcServerEntry router, void *connection)
316 {
317   SilcClientEntry client;
318
319   SILC_LOG_DEBUG(("Adding new client entry"));
320
321   client = silc_calloc(1, sizeof(*client));
322   client->nickname = nickname;
323   client->username = username;
324   client->userinfo = userinfo;
325   client->id = id;
326   client->router = router;
327   client->connection = connection;
328   client->channels = silc_hash_table_alloc(3, silc_hash_ptr, NULL,
329                                            NULL, NULL, NULL, NULL, TRUE);
330
331   if (!silc_idcache_add(id_list->clients, nickname, (void *)client->id, 
332                         (void *)client, FALSE)) {
333     silc_hash_table_free(client->channels);
334     silc_free(client);
335     return NULL;
336   }
337
338   return client;
339 }
340
341 /* Free client entry. This free's everything and removes the entry
342    from ID cache. Call silc_idlist_del_data before calling this one. */
343
344 int silc_idlist_del_client(SilcIDList id_list, SilcClientEntry entry)
345 {
346   SILC_LOG_DEBUG(("Start"));
347
348   if (entry) {
349     /* Remove from cache */
350     if (entry->id)
351       if (!silc_idcache_del_by_context(id_list->clients, entry))
352         return FALSE;
353
354     /* Free data */
355     if (entry->nickname)
356       silc_free(entry->nickname);
357     if (entry->username)
358       silc_free(entry->username);
359     if (entry->userinfo)
360       silc_free(entry->userinfo);
361     if (entry->id)
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   if (!channel->hmac)
579     if (!silc_hmac_alloc("hmac-sha1-96", NULL, &channel->hmac)) {
580       silc_free(channel);
581       return NULL;
582     }
583
584   channel->user_list = silc_hash_table_alloc(3, silc_hash_ptr, NULL, NULL,
585                                              NULL, NULL, NULL, TRUE);
586
587   if (!silc_idcache_add(id_list->channels, channel->channel_name, 
588                         (void *)channel->id, (void *)channel, FALSE)) {
589     silc_hmac_free(channel->hmac);
590     silc_hash_table_free(channel->user_list);
591     silc_free(channel);
592     return NULL;
593   }
594
595   return channel;
596 }
597
598 /* Foreach callbcak to free all users from the channel when deleting a
599    channel entry. */
600
601 static void silc_idlist_del_channel_foreach(void *key, void *context,
602                                             void *user_context)
603 {
604   SilcChannelClientEntry chl = (SilcChannelClientEntry)context;
605
606   /* Remove the context from the client's channel hash table as that
607      table and channel's user_list hash table share this same context. */
608   silc_hash_table_del(chl->client->channels, chl->channel);
609   silc_free(chl);
610 }
611
612 /* Free channel entry.  This free's everything. */
613
614 int silc_idlist_del_channel(SilcIDList id_list, SilcChannelEntry entry)
615 {
616   SILC_LOG_DEBUG(("Start"));
617
618   if (entry) {
619     /* Remove from cache */
620     if (entry->id)
621       if (!silc_idcache_del_by_id(id_list->channels, (void *)entry->id))
622         return FALSE;
623
624     /* Free data */
625     if (entry->channel_name)
626       silc_free(entry->channel_name);
627     if (entry->id)
628       silc_free(entry->id);
629     if (entry->topic)
630       silc_free(entry->topic);
631     if (entry->channel_key)
632       silc_cipher_free(entry->channel_key);
633     if (entry->key) {
634       memset(entry->key, 0, entry->key_len / 8);
635       silc_free(entry->key);
636     }
637     if (entry->cipher)
638       silc_free(entry->cipher);
639     if (entry->hmac_name)
640       silc_free(entry->hmac_name);
641     if (entry->rekey)
642       silc_free(entry->rekey);
643
644     /* Free all client entrys from the users list. The silc_hash_table_free
645        will free all the entries so they are not freed at the foreach 
646        callback. */
647     silc_hash_table_foreach(entry->user_list, silc_idlist_del_channel_foreach,
648                             NULL);
649     silc_hash_table_free(entry->user_list);
650
651     memset(entry, 'F', sizeof(*entry));
652     silc_free(entry);
653     return TRUE;
654   }
655
656   return FALSE;
657 }
658
659 /* Finds channel by channel name. Channel names are unique and they
660    are not case-sensitive. */
661
662 SilcChannelEntry
663 silc_idlist_find_channel_by_name(SilcIDList id_list, char *name,
664                                  SilcIDCacheEntry *ret_entry)
665 {
666   SilcIDCacheEntry id_cache = NULL;
667
668   SILC_LOG_DEBUG(("Channel by name"));
669
670   if (!silc_idcache_find_by_name_one(id_list->channels, name, &id_cache))
671     return NULL;
672
673   if (ret_entry)
674     *ret_entry = id_cache;
675
676   SILC_LOG_DEBUG(("Found"));
677
678   return id_cache->context;
679 }
680
681 /* Finds channel by Channel ID. */
682
683 SilcChannelEntry
684 silc_idlist_find_channel_by_id(SilcIDList id_list, SilcChannelID *id,
685                                SilcIDCacheEntry *ret_entry)
686 {
687   SilcIDCacheEntry id_cache = NULL;
688   SilcChannelEntry channel;
689
690   if (!id)
691     return NULL;
692
693   SILC_LOG_DEBUG(("Channel ID (%s)",
694                   silc_id_render(id, SILC_ID_CHANNEL)));
695
696   if (!silc_idcache_find_by_id_one(id_list->channels, (void *)id, &id_cache))
697     return NULL;
698
699   channel = (SilcChannelEntry)id_cache->context;
700
701   if (ret_entry)
702     *ret_entry = id_cache;
703
704   SILC_LOG_DEBUG(("Found"));
705
706   return channel;
707 }
708
709 /* Replaces old Channel ID with new one. This is done when router forces
710    normal server to change Channel ID. */
711
712 SilcChannelEntry
713 silc_idlist_replace_channel_id(SilcIDList id_list, SilcChannelID *old_id,
714                                SilcChannelID *new_id)
715 {
716   SilcIDCacheEntry id_cache = NULL;
717   SilcChannelEntry channel;
718
719   if (!old_id || !new_id)
720     return NULL;
721
722   SILC_LOG_DEBUG(("Replacing Channel ID"));
723
724   if (!silc_idcache_find_by_id_one(id_list->channels, (void *)old_id, 
725                                    &id_cache))
726     return NULL;
727
728   channel = (SilcChannelEntry)id_cache->context;
729
730   /* Remove the old entry and add a new one */
731
732   silc_idcache_del_by_id(id_list->channels, (void *)channel->id);
733
734   silc_free(channel->id);
735   channel->id = new_id;
736
737   silc_idcache_add(id_list->channels, channel->channel_name, channel->id, 
738                    channel, FALSE);
739
740   SILC_LOG_DEBUG(("Replaced"));
741
742   return channel;
743 }
744
745 /* Returns channels from the ID list. If the `channel_id' is NULL then
746    all channels are returned. */
747
748 SilcChannelEntry *
749 silc_idlist_get_channels(SilcIDList id_list, SilcChannelID *channel_id,
750                          uint32 *channels_count)
751 {
752   SilcIDCacheList list = NULL;
753   SilcIDCacheEntry id_cache = NULL;
754   SilcChannelEntry *channels = NULL;
755   int i = 0;
756
757   SILC_LOG_DEBUG(("Start"));
758
759   if (!channel_id) {
760     if (!silc_idcache_get_all(id_list->channels, &list))
761       return NULL;
762
763     channels = silc_calloc(silc_idcache_list_count(list), sizeof(*channels));
764     
765     i = 0;
766     silc_idcache_list_first(list, &id_cache);
767     channels[i++] = (SilcChannelEntry)id_cache->context;
768     
769     while (silc_idcache_list_next(list, &id_cache))
770       channels[i++] = (SilcChannelEntry)id_cache->context;
771     
772     silc_idcache_list_free(list);
773   } else {
774     if (!silc_idcache_find_by_id_one(id_list->channels, channel_id, &id_cache))
775       return NULL;
776
777     i = 1;
778     channels = silc_calloc(1, sizeof(*channels));
779     channels[0] = (SilcChannelEntry)id_cache->context;
780   }
781
782   if (channels_count)
783     *channels_count = i;
784
785   return channels;
786 }