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