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->hash)
75     silc_hash_free(idata->hash);
76   if (idata->public_key)
77     silc_pkcs_public_key_free(idata->public_key);
78 }
79
80 /* Purges ID cache */
81
82 SILC_TASK_CALLBACK_GLOBAL(silc_idlist_purge)
83 {
84   SilcIDListPurge i = (SilcIDListPurge)context;
85
86   SILC_LOG_DEBUG(("Start"));
87
88   silc_idcache_purge(i->cache);
89   silc_schedule_task_add(i->schedule, 0, 
90                          silc_idlist_purge,
91                          (void *)i, 600, 0,
92                          SILC_TASK_TIMEOUT, SILC_TASK_PRI_LOW);
93 }
94
95 /******************************************************************************
96
97                           Server entry functions
98
99 ******************************************************************************/
100
101 /* Add new server entry. This adds the new server entry to ID cache and
102    returns the allocated entry object or NULL on error. This is called
103    when new server connects to us. We also add ourselves to cache with
104    this function. */
105
106 SilcServerEntry 
107 silc_idlist_add_server(SilcIDList id_list, 
108                        char *server_name, int server_type,
109                        SilcServerID *id, SilcServerEntry router,
110                        void *connection)
111 {
112   SilcServerEntry server;
113
114   SILC_LOG_DEBUG(("Adding new server entry"));
115
116   server = silc_calloc(1, sizeof(*server));
117   server->server_name = server_name;
118   server->server_type = server_type;
119   server->id = id;
120   server->router = router;
121   server->connection = connection;
122
123   if (!silc_idcache_add(id_list->servers, server->server_name, 
124                         (void *)server->id, (void *)server, FALSE)) {
125     silc_free(server);
126     return NULL;
127   }
128
129   return server;
130 }
131
132 /* Finds server by Server ID */
133
134 SilcServerEntry
135 silc_idlist_find_server_by_id(SilcIDList id_list, SilcServerID *id,
136                               bool registered, SilcIDCacheEntry *ret_entry)
137 {
138   SilcIDCacheEntry id_cache = NULL;
139   SilcServerEntry server;
140
141   if (!id)
142     return NULL;
143
144   SILC_LOG_DEBUG(("Server ID (%s)",
145                   silc_id_render(id, SILC_ID_SERVER)));
146
147   if (!silc_idcache_find_by_id_one(id_list->servers, (void *)id, 
148                                    &id_cache))
149     return NULL;
150
151   server = (SilcServerEntry)id_cache->context;
152
153   if (ret_entry)
154     *ret_entry = id_cache;
155
156   if (server && registered && 
157       !(server->data.status & SILC_IDLIST_STATUS_REGISTERED))
158     return NULL;
159
160   SILC_LOG_DEBUG(("Found"));
161
162   return server;
163 }
164
165 /* Find server by name */
166
167 SilcServerEntry
168 silc_idlist_find_server_by_name(SilcIDList id_list, char *name,
169                                 bool registered, SilcIDCacheEntry *ret_entry)
170 {
171   SilcIDCacheEntry id_cache = NULL;
172   SilcServerEntry server;
173
174   SILC_LOG_DEBUG(("Server by name `%s'", name));
175
176   if (!silc_idcache_find_by_name_one(id_list->servers, name, &id_cache))
177     return NULL;
178
179   server = (SilcServerEntry)id_cache->context;
180   
181   if (ret_entry)
182     *ret_entry = id_cache;
183
184   if (server && registered &&
185       !(server->data.status & SILC_IDLIST_STATUS_REGISTERED))
186     return NULL;
187
188   SILC_LOG_DEBUG(("Found"));
189
190   return server;
191 }
192
193 /* Find server by connection parameters, hostname and port */
194
195 SilcServerEntry
196 silc_idlist_find_server_by_conn(SilcIDList id_list, char *hostname,
197                                 int port, bool registered,
198                                 SilcIDCacheEntry *ret_entry)
199 {
200   SilcIDCacheList list = NULL;
201   SilcIDCacheEntry id_cache = NULL;
202   SilcServerEntry server = NULL;
203   SilcSocketConnection sock;
204  
205   SILC_LOG_DEBUG(("Server by hostname %s and port %d", hostname, port));
206
207   if (!silc_idcache_get_all(id_list->servers, &list))
208     return NULL;
209
210   if (!silc_idcache_list_first(list, &id_cache)) {
211     silc_idcache_list_free(list);
212     return NULL;
213   }
214
215   while (id_cache) {
216     server = (SilcServerEntry)id_cache->context;
217     sock = (SilcSocketConnection)server->connection;
218     
219     if (sock && ((sock->hostname && !strcasecmp(sock->hostname, hostname)) ||
220                  (sock->ip && !strcasecmp(sock->ip, hostname)))
221         && sock->port == port)
222       break;
223
224     id_cache = NULL;
225     server = NULL;
226
227     if (!silc_idcache_list_next(list, &id_cache))
228       break;
229   }
230   
231   silc_idcache_list_free(list);
232
233   if (ret_entry)
234     *ret_entry = id_cache;
235
236   if (server && registered &&
237       !(server->data.status & SILC_IDLIST_STATUS_REGISTERED))
238     return NULL;
239
240   SILC_LOG_DEBUG(("Found"));
241
242   return server;
243 }
244
245 /* Replaces old Server ID with new one */ 
246
247 SilcServerEntry
248 silc_idlist_replace_server_id(SilcIDList id_list, SilcServerID *old_id,
249                               SilcServerID *new_id)
250 {
251   SilcIDCacheEntry id_cache = NULL;
252   SilcServerEntry server;
253
254   if (!old_id || !new_id)
255     return NULL;
256
257   SILC_LOG_DEBUG(("Replacing Server ID"));
258
259   if (!silc_idcache_find_by_id_one(id_list->servers, (void *)old_id, 
260                                    &id_cache))
261     return NULL;
262
263   server = (SilcServerEntry)id_cache->context;
264
265   /* Remove the old entry and add a new one */
266
267   silc_idcache_del_by_id(id_list->servers, (void *)server->id);
268
269   silc_free(server->id);
270   server->id = new_id;
271
272   silc_idcache_add(id_list->servers, server->server_name, server->id, 
273                    server, FALSE);
274
275   SILC_LOG_DEBUG(("Found"));
276
277   return server;
278 }
279
280 /* Removes and free's server entry from ID list */
281
282 int silc_idlist_del_server(SilcIDList id_list, SilcServerEntry entry)
283 {
284   SILC_LOG_DEBUG(("Start"));
285
286   if (entry) {
287     /* Remove from cache */
288     if (entry->id)
289       if (!silc_idcache_del_by_id(id_list->servers, (void *)entry->id))
290         return FALSE;
291
292     /* Free data */
293     silc_free(entry->server_name);
294     silc_free(entry->id);
295
296     memset(entry, 'F', sizeof(*entry));
297     silc_free(entry);
298     return TRUE;
299   }
300
301   return FALSE;
302 }
303
304 /******************************************************************************
305
306                           Client entry functions
307
308 ******************************************************************************/
309
310 /* Add new client entry. This adds the client entry to ID cache system
311    and returns the allocated client entry or NULL on error.  This is
312    called when new client connection is accepted to the server. If The
313    `router' is provided then the all server routines assume that the client
314    is not directly connected local client but it has router set and is
315    remote.  If this is the case then `connection' must be NULL.  If, on the
316    other hand, the `connection' is provided then the client is assumed
317    to be directly connected local client and `router' must be NULL. */
318
319 SilcClientEntry
320 silc_idlist_add_client(SilcIDList id_list, char *nickname, char *username, 
321                        char *userinfo, SilcClientID *id, 
322                        SilcServerEntry router, void *connection)
323 {
324   SilcClientEntry client;
325
326   SILC_LOG_DEBUG(("Adding new client entry"));
327
328   client = silc_calloc(1, sizeof(*client));
329   client->nickname = nickname;
330   client->username = username;
331   client->userinfo = userinfo;
332   client->id = id;
333   client->router = router;
334   client->connection = connection;
335   client->channels = silc_hash_table_alloc(3, silc_hash_ptr, NULL,
336                                            NULL, NULL, NULL, NULL, TRUE);
337
338   if (!silc_idcache_add(id_list->clients, nickname, (void *)client->id, 
339                         (void *)client, FALSE)) {
340     silc_hash_table_free(client->channels);
341     silc_free(client);
342     return NULL;
343   }
344
345   return client;
346 }
347
348 /* Free client entry. This free's everything and removes the entry
349    from ID cache. Call silc_idlist_del_data before calling this one. */
350
351 int silc_idlist_del_client(SilcIDList id_list, SilcClientEntry entry)
352 {
353   SILC_LOG_DEBUG(("Start"));
354
355   if (entry) {
356     /* Remove from cache */
357     if (entry->id)
358       if (!silc_idcache_del_by_context(id_list->clients, entry))
359         return FALSE;
360
361     /* Free data */
362     silc_free(entry->nickname);
363     silc_free(entry->username);
364     silc_free(entry->userinfo);
365     silc_free(entry->id);
366     silc_hash_table_free(entry->channels);
367
368     memset(entry, 'F', sizeof(*entry));
369     silc_free(entry);
370
371     return TRUE;
372   }
373
374   return FALSE;
375 }
376
377 /* Returns all clients matching requested nickname. Number of clients is
378    returned to `clients_count'. Caller must free the returned table. */
379
380 int silc_idlist_get_clients_by_nickname(SilcIDList id_list, char *nickname,
381                                         char *server, 
382                                         SilcClientEntry **clients,
383                                         uint32 *clients_count)
384 {
385   SilcIDCacheList list = NULL;
386   SilcIDCacheEntry id_cache = NULL;
387
388   SILC_LOG_DEBUG(("Start"));
389
390   if (!silc_idcache_find_by_name(id_list->clients, nickname, &list))
391     return FALSE;
392
393   *clients = silc_realloc(*clients, 
394                           (silc_idcache_list_count(list) + *clients_count) * 
395                           sizeof(**clients));
396
397   silc_idcache_list_first(list, &id_cache);
398   (*clients)[(*clients_count)++] = (SilcClientEntry)id_cache->context;
399
400   while (silc_idcache_list_next(list, &id_cache))
401     (*clients)[(*clients_count)++] = (SilcClientEntry)id_cache->context;
402   
403   silc_idcache_list_free(list);
404   
405   SILC_LOG_DEBUG(("Found total %d clients", *clients_count));
406
407   return TRUE;
408 }
409
410 /* Returns all clients matching requested nickname hash. Number of clients
411    is returned to `clients_count'. Caller must free the returned table. */
412
413 int silc_idlist_get_clients_by_hash(SilcIDList id_list, char *nickname,
414                                     SilcHash md5hash,
415                                     SilcClientEntry **clients,
416                                     uint32 *clients_count)
417 {
418   SilcIDCacheList list = NULL;
419   SilcIDCacheEntry id_cache = NULL;
420   unsigned char hash[32];
421   SilcClientID client_id;
422
423   SILC_LOG_DEBUG(("Start"));
424
425   silc_hash_make(md5hash, nickname, strlen(nickname), hash);
426
427   /* As the Client ID is hashed in the ID cache by hashing only the hash
428      from the Client ID, we can do a lookup with only the hash not the
429      other parts of the ID and get all the clients with that hash, ie.
430      with that nickname, as the hash is from the nickname. */
431   memset(&client_id, 0, sizeof(client_id));
432   memcpy(&client_id.hash, hash, sizeof(client_id.hash));
433   if (!silc_idcache_find_by_id(id_list->clients, &client_id, &list))
434     return FALSE;
435
436   *clients = silc_realloc(*clients, 
437                           (silc_idcache_list_count(list) + *clients_count) * 
438                           sizeof(**clients));
439
440   silc_idcache_list_first(list, &id_cache);
441   (*clients)[(*clients_count)++] = (SilcClientEntry)id_cache->context;
442
443   while (silc_idcache_list_next(list, &id_cache))
444     (*clients)[(*clients_count)++] = (SilcClientEntry)id_cache->context;
445   
446   silc_idcache_list_free(list);
447   
448   SILC_LOG_DEBUG(("Found total %d clients", *clients_count));
449
450   return TRUE;
451 }
452
453 /* Finds client by Client ID */
454
455 SilcClientEntry
456 silc_idlist_find_client_by_id(SilcIDList id_list, SilcClientID *id,
457                               bool registered, SilcIDCacheEntry *ret_entry)
458 {
459   SilcIDCacheEntry id_cache = NULL;
460   SilcClientEntry client;
461
462   if (!id)
463     return NULL;
464
465   SILC_LOG_DEBUG(("Client ID (%s)", 
466                   silc_id_render(id, SILC_ID_CLIENT)));
467
468   /* Do extended search since the normal ID comparison function for
469      Client ID's compares only the hash from the Client ID and not the
470      entire ID. The silc_hash_client_id_compare compares the entire
471      Client ID as we want to find one specific Client ID. */
472   if (!silc_idcache_find_by_id_one_ext(id_list->clients, (void *)id, 
473                                        NULL, NULL, 
474                                        silc_hash_client_id_compare, NULL,
475                                        &id_cache))
476     return NULL;
477
478   client = (SilcClientEntry)id_cache->context;
479
480   if (ret_entry)
481     *ret_entry = id_cache;
482
483   if (client && registered &&
484       !(client->data.status & SILC_IDLIST_STATUS_REGISTERED))
485     return NULL;
486
487   SILC_LOG_DEBUG(("Found"));
488
489   return client;
490 }
491
492 /* Replaces old Client ID with new one */
493
494 SilcClientEntry
495 silc_idlist_replace_client_id(SilcIDList id_list, SilcClientID *old_id,
496                               SilcClientID *new_id)
497 {
498   SilcIDCacheEntry id_cache = NULL;
499   SilcClientEntry client;
500
501   if (!old_id || !new_id)
502     return NULL;
503
504   SILC_LOG_DEBUG(("Replacing Client ID"));
505
506   /* Do extended search since the normal ID comparison function for
507      Client ID's compares only the hash from the Client ID and not the
508      entire ID. The silc_hash_client_id_compare compares the entire
509      Client ID as we want to find one specific Client ID. */
510   if (!silc_idcache_find_by_id_one_ext(id_list->clients, (void *)old_id, 
511                                        NULL, NULL, 
512                                        silc_hash_client_id_compare, NULL,
513                                        &id_cache))
514     return NULL;
515
516   client = (SilcClientEntry)id_cache->context;
517
518   /* Remove the old entry and add a new one */
519
520   silc_idcache_del_by_context(id_list->clients, client);
521
522   silc_free(client->id);
523   client->id = new_id;
524
525   silc_idcache_add(id_list->clients, NULL, client->id, client, FALSE);
526
527   SILC_LOG_DEBUG(("Replaced"));
528
529   return client;
530 }
531
532 /* Client cache entry destructor that is called when the cache is purged. */
533
534 void silc_idlist_client_destructor(SilcIDCache cache,
535                                    SilcIDCacheEntry entry)
536 {
537   SilcClientEntry client;
538
539   client = (SilcClientEntry)entry->context;
540   if (client) {
541     silc_free(client->nickname);
542     silc_free(client->username);
543     silc_free(client->userinfo);
544     silc_free(client->id);
545     silc_hash_table_free(client->channels);
546
547     memset(client, 'F', sizeof(*client));
548     silc_free(client);
549   }
550 }
551
552 /******************************************************************************
553
554                           Channel entry functions
555
556 ******************************************************************************/
557
558 /* Add new channel entry. This add the new channel entry to the ID cache
559    system and returns the allocated entry or NULL on error. */
560
561 SilcChannelEntry
562 silc_idlist_add_channel(SilcIDList id_list, char *channel_name, int mode,
563                         SilcChannelID *id, SilcServerEntry router,
564                         SilcCipher channel_key, SilcHmac hmac)
565 {
566   SilcChannelEntry channel;
567
568   SILC_LOG_DEBUG(("Adding new channel entry"));
569
570   channel = silc_calloc(1, sizeof(*channel));
571   channel->channel_name = channel_name;
572   channel->mode = mode;
573   channel->id = id;
574   channel->router = router;
575   channel->channel_key = channel_key;
576   channel->hmac = hmac;
577   channel->created = time(0);
578   if (!channel->hmac)
579     if (!silc_hmac_alloc(SILC_DEFAULT_HMAC, 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     silc_free(entry->channel_name);
626     silc_free(entry->id);
627     silc_free(entry->topic);
628     if (entry->channel_key)
629       silc_cipher_free(entry->channel_key);
630     if (entry->key) {
631       memset(entry->key, 0, entry->key_len / 8);
632       silc_free(entry->key);
633     }
634     silc_free(entry->cipher);
635     if (entry->hmac)
636       silc_hmac_free(entry->hmac);
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 }