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, i->timeout, 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, 0, NULL)) {
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, 0, NULL);
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                        int expire)
324 {
325   SilcClientEntry client;
326
327   SILC_LOG_DEBUG(("Adding new client entry"));
328
329   client = silc_calloc(1, sizeof(*client));
330   client->nickname = nickname;
331   client->username = username;
332   client->userinfo = userinfo;
333   client->id = id;
334   client->router = router;
335   client->connection = connection;
336   client->channels = silc_hash_table_alloc(3, silc_hash_ptr, NULL,
337                                            NULL, NULL, NULL, NULL, TRUE);
338
339   if (!silc_idcache_add(id_list->clients, nickname, (void *)client->id, 
340                         (void *)client, expire, NULL)) {
341     silc_hash_table_free(client->channels);
342     silc_free(client);
343     return NULL;
344   }
345
346   return client;
347 }
348
349 /* Free client entry. This free's everything and removes the entry
350    from ID cache. Call silc_idlist_del_data before calling this one. */
351
352 int silc_idlist_del_client(SilcIDList id_list, SilcClientEntry entry)
353 {
354   SILC_LOG_DEBUG(("Start"));
355
356   if (entry) {
357     /* Remove from cache */
358     if (entry->id)
359       if (!silc_idcache_del_by_context(id_list->clients, entry))
360         return FALSE;
361
362     /* Free data */
363     silc_free(entry->nickname);
364     silc_free(entry->username);
365     silc_free(entry->userinfo);
366     silc_free(entry->id);
367     silc_hash_table_free(entry->channels);
368
369     memset(entry, 'F', sizeof(*entry));
370     silc_free(entry);
371
372     return TRUE;
373   }
374
375   return FALSE;
376 }
377
378 /* Returns all clients matching requested nickname. Number of clients is
379    returned to `clients_count'. Caller must free the returned table. */
380
381 int silc_idlist_get_clients_by_nickname(SilcIDList id_list, char *nickname,
382                                         char *server, 
383                                         SilcClientEntry **clients,
384                                         uint32 *clients_count)
385 {
386   SilcIDCacheList list = NULL;
387   SilcIDCacheEntry id_cache = NULL;
388
389   SILC_LOG_DEBUG(("Start"));
390
391   if (!silc_idcache_find_by_name(id_list->clients, nickname, &list))
392     return FALSE;
393
394   *clients = silc_realloc(*clients, 
395                           (silc_idcache_list_count(list) + *clients_count) * 
396                           sizeof(**clients));
397
398   silc_idcache_list_first(list, &id_cache);
399   (*clients)[(*clients_count)++] = (SilcClientEntry)id_cache->context;
400
401   while (silc_idcache_list_next(list, &id_cache))
402     (*clients)[(*clients_count)++] = (SilcClientEntry)id_cache->context;
403   
404   silc_idcache_list_free(list);
405   
406   SILC_LOG_DEBUG(("Found total %d clients", *clients_count));
407
408   return TRUE;
409 }
410
411 /* Returns all clients matching requested nickname hash. Number of clients
412    is returned to `clients_count'. Caller must free the returned table. */
413
414 int silc_idlist_get_clients_by_hash(SilcIDList id_list, char *nickname,
415                                     SilcHash md5hash,
416                                     SilcClientEntry **clients,
417                                     uint32 *clients_count)
418 {
419   SilcIDCacheList list = NULL;
420   SilcIDCacheEntry id_cache = NULL;
421   unsigned char hash[32];
422   SilcClientID client_id;
423
424   SILC_LOG_DEBUG(("Start"));
425
426   silc_hash_make(md5hash, nickname, strlen(nickname), hash);
427
428   /* As the Client ID is hashed in the ID cache by hashing only the hash
429      from the Client ID, we can do a lookup with only the hash not the
430      other parts of the ID and get all the clients with that hash, ie.
431      with that nickname, as the hash is from the nickname. */
432   memset(&client_id, 0, sizeof(client_id));
433   memcpy(&client_id.hash, hash, sizeof(client_id.hash));
434   if (!silc_idcache_find_by_id(id_list->clients, &client_id, &list))
435     return FALSE;
436
437   *clients = silc_realloc(*clients, 
438                           (silc_idcache_list_count(list) + *clients_count) * 
439                           sizeof(**clients));
440
441   silc_idcache_list_first(list, &id_cache);
442   (*clients)[(*clients_count)++] = (SilcClientEntry)id_cache->context;
443
444   while (silc_idcache_list_next(list, &id_cache))
445     (*clients)[(*clients_count)++] = (SilcClientEntry)id_cache->context;
446   
447   silc_idcache_list_free(list);
448   
449   SILC_LOG_DEBUG(("Found total %d clients", *clients_count));
450
451   return TRUE;
452 }
453
454 /* Finds client by Client ID */
455
456 SilcClientEntry
457 silc_idlist_find_client_by_id(SilcIDList id_list, SilcClientID *id,
458                               bool registered, SilcIDCacheEntry *ret_entry)
459 {
460   SilcIDCacheEntry id_cache = NULL;
461   SilcClientEntry client;
462
463   if (!id)
464     return NULL;
465
466   SILC_LOG_DEBUG(("Client ID (%s)", 
467                   silc_id_render(id, SILC_ID_CLIENT)));
468
469   /* Do extended search since the normal ID comparison function for
470      Client ID's compares only the hash from the Client ID and not the
471      entire ID. The silc_hash_client_id_compare compares the entire
472      Client ID as we want to find one specific Client ID. */
473   if (!silc_idcache_find_by_id_one_ext(id_list->clients, (void *)id, 
474                                        NULL, NULL, 
475                                        silc_hash_client_id_compare, NULL,
476                                        &id_cache))
477     return NULL;
478
479   client = (SilcClientEntry)id_cache->context;
480
481   if (ret_entry)
482     *ret_entry = id_cache;
483
484   if (client && registered &&
485       !(client->data.status & SILC_IDLIST_STATUS_REGISTERED))
486     return NULL;
487
488   SILC_LOG_DEBUG(("Found"));
489
490   return client;
491 }
492
493 /* Replaces old Client ID with new one */
494
495 SilcClientEntry
496 silc_idlist_replace_client_id(SilcIDList id_list, SilcClientID *old_id,
497                               SilcClientID *new_id)
498 {
499   SilcIDCacheEntry id_cache = NULL;
500   SilcClientEntry client;
501
502   if (!old_id || !new_id)
503     return NULL;
504
505   SILC_LOG_DEBUG(("Replacing Client ID"));
506
507   /* Do extended search since the normal ID comparison function for
508      Client ID's compares only the hash from the Client ID and not the
509      entire ID. The silc_hash_client_id_compare compares the entire
510      Client ID as we want to find one specific Client ID. */
511   if (!silc_idcache_find_by_id_one_ext(id_list->clients, (void *)old_id, 
512                                        NULL, NULL, 
513                                        silc_hash_client_id_compare, NULL,
514                                        &id_cache))
515     return NULL;
516
517   client = (SilcClientEntry)id_cache->context;
518
519   /* Remove the old entry and add a new one */
520
521   silc_idcache_del_by_context(id_list->clients, client);
522
523   silc_free(client->id);
524   client->id = new_id;
525
526   silc_idcache_add(id_list->clients, NULL, client->id, client, 0, NULL);
527
528   SILC_LOG_DEBUG(("Replaced"));
529
530   return client;
531 }
532
533 /* Client cache entry destructor that is called when the cache is purged. */
534
535 void silc_idlist_client_destructor(SilcIDCache cache,
536                                    SilcIDCacheEntry entry)
537 {
538   SilcClientEntry client;
539
540   client = (SilcClientEntry)entry->context;
541   if (client) {
542     silc_free(client->nickname);
543     silc_free(client->username);
544     silc_free(client->userinfo);
545     silc_free(client->id);
546     silc_hash_table_free(client->channels);
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                         int expire)
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, expire, NULL)) {
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     if (entry->hmac)
638       silc_hmac_free(entry->hmac);
639     silc_free(entry->hmac_name);
640     silc_free(entry->rekey);
641
642     /* Free all client entrys from the users list. The silc_hash_table_free
643        will free all the entries so they are not freed at the foreach 
644        callback. */
645     silc_hash_table_foreach(entry->user_list, silc_idlist_del_channel_foreach,
646                             NULL);
647     silc_hash_table_free(entry->user_list);
648
649     memset(entry, 'F', sizeof(*entry));
650     silc_free(entry);
651     return TRUE;
652   }
653
654   return FALSE;
655 }
656
657 /* Finds channel by channel name. Channel names are unique and they
658    are not case-sensitive. */
659
660 SilcChannelEntry
661 silc_idlist_find_channel_by_name(SilcIDList id_list, char *name,
662                                  SilcIDCacheEntry *ret_entry)
663 {
664   SilcIDCacheEntry id_cache = NULL;
665
666   SILC_LOG_DEBUG(("Channel by name"));
667
668   if (!silc_idcache_find_by_name_one(id_list->channels, name, &id_cache))
669     return NULL;
670
671   if (ret_entry)
672     *ret_entry = id_cache;
673
674   SILC_LOG_DEBUG(("Found"));
675
676   return id_cache->context;
677 }
678
679 /* Finds channel by Channel ID. */
680
681 SilcChannelEntry
682 silc_idlist_find_channel_by_id(SilcIDList id_list, SilcChannelID *id,
683                                SilcIDCacheEntry *ret_entry)
684 {
685   SilcIDCacheEntry id_cache = NULL;
686   SilcChannelEntry channel;
687
688   if (!id)
689     return NULL;
690
691   SILC_LOG_DEBUG(("Channel ID (%s)",
692                   silc_id_render(id, SILC_ID_CHANNEL)));
693
694   if (!silc_idcache_find_by_id_one(id_list->channels, (void *)id, &id_cache))
695     return NULL;
696
697   channel = (SilcChannelEntry)id_cache->context;
698
699   if (ret_entry)
700     *ret_entry = id_cache;
701
702   SILC_LOG_DEBUG(("Found"));
703
704   return channel;
705 }
706
707 /* Replaces old Channel ID with new one. This is done when router forces
708    normal server to change Channel ID. */
709
710 SilcChannelEntry
711 silc_idlist_replace_channel_id(SilcIDList id_list, SilcChannelID *old_id,
712                                SilcChannelID *new_id)
713 {
714   SilcIDCacheEntry id_cache = NULL;
715   SilcChannelEntry channel;
716
717   if (!old_id || !new_id)
718     return NULL;
719
720   SILC_LOG_DEBUG(("Replacing Channel ID"));
721
722   if (!silc_idcache_find_by_id_one(id_list->channels, (void *)old_id, 
723                                    &id_cache))
724     return NULL;
725
726   channel = (SilcChannelEntry)id_cache->context;
727
728   /* Remove the old entry and add a new one */
729
730   silc_idcache_del_by_id(id_list->channels, (void *)channel->id);
731
732   silc_free(channel->id);
733   channel->id = new_id;
734
735   silc_idcache_add(id_list->channels, channel->channel_name, channel->id, 
736                    channel, 0, NULL);
737
738   SILC_LOG_DEBUG(("Replaced"));
739
740   return channel;
741 }
742
743 /* Returns channels from the ID list. If the `channel_id' is NULL then
744    all channels are returned. */
745
746 SilcChannelEntry *
747 silc_idlist_get_channels(SilcIDList id_list, SilcChannelID *channel_id,
748                          uint32 *channels_count)
749 {
750   SilcIDCacheList list = NULL;
751   SilcIDCacheEntry id_cache = NULL;
752   SilcChannelEntry *channels = NULL;
753   int i = 0;
754
755   SILC_LOG_DEBUG(("Start"));
756
757   if (!channel_id) {
758     if (!silc_idcache_get_all(id_list->channels, &list))
759       return NULL;
760
761     channels = silc_calloc(silc_idcache_list_count(list), sizeof(*channels));
762     
763     i = 0;
764     silc_idcache_list_first(list, &id_cache);
765     channels[i++] = (SilcChannelEntry)id_cache->context;
766     
767     while (silc_idcache_list_next(list, &id_cache))
768       channels[i++] = (SilcChannelEntry)id_cache->context;
769     
770     silc_idcache_list_free(list);
771   } else {
772     if (!silc_idcache_find_by_id_one(id_list->channels, channel_id, &id_cache))
773       return NULL;
774
775     i = 1;
776     channels = silc_calloc(1, sizeof(*channels));
777     channels[0] = (SilcChannelEntry)id_cache->context;
778   }
779
780   if (channels_count)
781     *channels_count = i;
782
783   return channels;
784 }