Integer type name change.
[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                                         SilcUInt32 *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                                     SilcUInt32 *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   SILC_LOG_DEBUG(("Removing client %s from channel %s",
609                   chl->client->nickname, chl->channel->channel_name));
610
611   /* Remove the context from the client's channel hash table as that
612      table and channel's user_list hash table share this same context. */
613   silc_hash_table_del(chl->client->channels, chl->channel);
614   silc_free(chl);
615 }
616
617 /* Free channel entry.  This free's everything. */
618
619 int silc_idlist_del_channel(SilcIDList id_list, SilcChannelEntry entry)
620 {
621   SILC_LOG_DEBUG(("Start"));
622
623   if (entry) {
624     /* Remove from cache */
625     if (entry->id)
626       if (!silc_idcache_del_by_id(id_list->channels, (void *)entry->id))
627         return FALSE;
628
629     /* Free all client entrys from the users list. The silc_hash_table_free
630        will free all the entries so they are not freed at the foreach 
631        callback. */
632     silc_hash_table_foreach(entry->user_list, silc_idlist_del_channel_foreach,
633                             NULL);
634     silc_hash_table_free(entry->user_list);
635
636     /* Free data */
637     silc_free(entry->channel_name);
638     silc_free(entry->id);
639     silc_free(entry->topic);
640     if (entry->channel_key)
641       silc_cipher_free(entry->channel_key);
642     if (entry->key) {
643       memset(entry->key, 0, entry->key_len / 8);
644       silc_free(entry->key);
645     }
646     silc_free(entry->cipher);
647     if (entry->hmac)
648       silc_hmac_free(entry->hmac);
649     silc_free(entry->hmac_name);
650     silc_free(entry->rekey);
651
652     memset(entry, 'F', sizeof(*entry));
653     silc_free(entry);
654     return TRUE;
655   }
656
657   return FALSE;
658 }
659
660 /* Finds channel by channel name. Channel names are unique and they
661    are not case-sensitive. */
662
663 SilcChannelEntry
664 silc_idlist_find_channel_by_name(SilcIDList id_list, char *name,
665                                  SilcIDCacheEntry *ret_entry)
666 {
667   SilcIDCacheEntry id_cache = NULL;
668
669   SILC_LOG_DEBUG(("Channel by name %s", name));
670
671   if (!silc_idcache_find_by_name_one(id_list->channels, name, &id_cache))
672     return NULL;
673
674   if (ret_entry)
675     *ret_entry = id_cache;
676
677   SILC_LOG_DEBUG(("Found"));
678
679   return id_cache->context;
680 }
681
682 /* Finds channel by Channel ID. */
683
684 SilcChannelEntry
685 silc_idlist_find_channel_by_id(SilcIDList id_list, SilcChannelID *id,
686                                SilcIDCacheEntry *ret_entry)
687 {
688   SilcIDCacheEntry id_cache = NULL;
689   SilcChannelEntry channel;
690
691   if (!id)
692     return NULL;
693
694   SILC_LOG_DEBUG(("Channel ID (%s)",
695                   silc_id_render(id, SILC_ID_CHANNEL)));
696
697   if (!silc_idcache_find_by_id_one(id_list->channels, (void *)id, &id_cache))
698     return NULL;
699
700   channel = (SilcChannelEntry)id_cache->context;
701
702   if (ret_entry)
703     *ret_entry = id_cache;
704
705   SILC_LOG_DEBUG(("Found"));
706
707   return channel;
708 }
709
710 /* Replaces old Channel ID with new one. This is done when router forces
711    normal server to change Channel ID. */
712
713 SilcChannelEntry
714 silc_idlist_replace_channel_id(SilcIDList id_list, SilcChannelID *old_id,
715                                SilcChannelID *new_id)
716 {
717   SilcIDCacheEntry id_cache = NULL;
718   SilcChannelEntry channel;
719
720   if (!old_id || !new_id)
721     return NULL;
722
723   SILC_LOG_DEBUG(("Replacing Channel ID"));
724
725   if (!silc_idcache_find_by_id_one(id_list->channels, (void *)old_id, 
726                                    &id_cache))
727     return NULL;
728
729   channel = (SilcChannelEntry)id_cache->context;
730
731   /* Remove the old entry and add a new one */
732
733   silc_idcache_del_by_id(id_list->channels, (void *)channel->id);
734
735   silc_free(channel->id);
736   channel->id = new_id;
737
738   silc_idcache_add(id_list->channels, channel->channel_name, channel->id, 
739                    channel, 0, NULL);
740
741   SILC_LOG_DEBUG(("Replaced"));
742
743   return channel;
744 }
745
746 /* Returns channels from the ID list. If the `channel_id' is NULL then
747    all channels are returned. */
748
749 SilcChannelEntry *
750 silc_idlist_get_channels(SilcIDList id_list, SilcChannelID *channel_id,
751                          SilcUInt32 *channels_count)
752 {
753   SilcIDCacheList list = NULL;
754   SilcIDCacheEntry id_cache = NULL;
755   SilcChannelEntry *channels = NULL;
756   int i = 0;
757
758   SILC_LOG_DEBUG(("Start"));
759
760   if (!channel_id) {
761     if (!silc_idcache_get_all(id_list->channels, &list))
762       return NULL;
763
764     channels = silc_calloc(silc_idcache_list_count(list), sizeof(*channels));
765     
766     i = 0;
767     silc_idcache_list_first(list, &id_cache);
768     channels[i++] = (SilcChannelEntry)id_cache->context;
769     
770     while (silc_idcache_list_next(list, &id_cache))
771       channels[i++] = (SilcChannelEntry)id_cache->context;
772     
773     silc_idcache_list_free(list);
774   } else {
775     if (!silc_idcache_find_by_id_one(id_list->channels, channel_id, &id_cache))
776       return NULL;
777
778     i = 1;
779     channels = silc_calloc(1, sizeof(*channels));
780     channels[0] = (SilcChannelEntry)id_cache->context;
781   }
782
783   if (channels_count)
784     *channels_count = i;
785
786   return channels;
787 }