Started implementing protocol version 1.1 and narrowing down
[silc.git] / apps / silcd / idlist.c
1 /*
2
3   idlist.c
4
5   Author: Pekka Riikonen <priikone@silcnet.org>
6
7   Copyright (C) 1997 - 2002 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, const char *nickname)
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   if (!silc_idcache_del_by_context(id_list->clients, client))
522     return NULL;
523
524   silc_free(client->id);
525   silc_free(client->nickname);
526   client->id = new_id;
527   client->nickname = nickname ? strdup(nickname) : NULL;
528
529   if (!silc_idcache_add(id_list->clients, client->nickname, client->id, 
530                         client, 0, NULL))
531     return NULL;
532
533   SILC_LOG_DEBUG(("Replaced"));
534
535   return client;
536 }
537
538 /* Client cache entry destructor that is called when the cache is purged. */
539
540 void silc_idlist_client_destructor(SilcIDCache cache,
541                                    SilcIDCacheEntry entry)
542 {
543   SilcClientEntry client;
544
545   client = (SilcClientEntry)entry->context;
546   if (client) {
547     silc_free(client->nickname);
548     silc_free(client->username);
549     silc_free(client->userinfo);
550     silc_free(client->id);
551     silc_hash_table_free(client->channels);
552
553     memset(client, 'F', sizeof(*client));
554     silc_free(client);
555   }
556 }
557
558 /******************************************************************************
559
560                           Channel entry functions
561
562 ******************************************************************************/
563
564 /* Add new channel entry. This add the new channel entry to the ID cache
565    system and returns the allocated entry or NULL on error. */
566
567 SilcChannelEntry
568 silc_idlist_add_channel(SilcIDList id_list, char *channel_name, int mode,
569                         SilcChannelID *id, SilcServerEntry router,
570                         SilcCipher channel_key, SilcHmac hmac,
571                         int expire)
572 {
573   SilcChannelEntry channel;
574
575   SILC_LOG_DEBUG(("Adding new channel entry"));
576
577   channel = silc_calloc(1, sizeof(*channel));
578   channel->channel_name = channel_name;
579   channel->mode = mode;
580   channel->id = id;
581   channel->router = router;
582   channel->channel_key = channel_key;
583   channel->hmac = hmac;
584   channel->created = time(0);
585   if (!channel->hmac)
586     if (!silc_hmac_alloc(SILC_DEFAULT_HMAC, NULL, &channel->hmac)) {
587       silc_free(channel);
588       return NULL;
589     }
590
591   channel->user_list = silc_hash_table_alloc(3, silc_hash_ptr, NULL, NULL,
592                                              NULL, NULL, NULL, TRUE);
593
594   if (!silc_idcache_add(id_list->channels, channel->channel_name, 
595                         (void *)channel->id, (void *)channel, expire, NULL)) {
596     silc_hmac_free(channel->hmac);
597     silc_hash_table_free(channel->user_list);
598     silc_free(channel);
599     return NULL;
600   }
601
602   return channel;
603 }
604
605 /* Foreach callbcak to free all users from the channel when deleting a
606    channel entry. */
607
608 static void silc_idlist_del_channel_foreach(void *key, void *context,
609                                             void *user_context)
610 {
611   SilcChannelClientEntry chl = (SilcChannelClientEntry)context;
612
613   SILC_LOG_DEBUG(("Removing client %s from channel %s",
614                   chl->client->nickname, chl->channel->channel_name));
615
616   /* Remove the context from the client's channel hash table as that
617      table and channel's user_list hash table share this same context. */
618   silc_hash_table_del(chl->client->channels, chl->channel);
619   silc_free(chl);
620 }
621
622 /* Free channel entry.  This free's everything. */
623
624 int silc_idlist_del_channel(SilcIDList id_list, SilcChannelEntry entry)
625 {
626   SILC_LOG_DEBUG(("Start"));
627
628   if (entry) {
629     /* Remove from cache */
630     if (entry->id)
631       if (!silc_idcache_del_by_id(id_list->channels, (void *)entry->id))
632         return FALSE;
633
634     /* Free all client entrys from the users list. The silc_hash_table_free
635        will free all the entries so they are not freed at the foreach 
636        callback. */
637     silc_hash_table_foreach(entry->user_list, silc_idlist_del_channel_foreach,
638                             NULL);
639     silc_hash_table_free(entry->user_list);
640
641     /* Free data */
642     silc_free(entry->channel_name);
643     silc_free(entry->id);
644     silc_free(entry->topic);
645     if (entry->channel_key)
646       silc_cipher_free(entry->channel_key);
647     if (entry->key) {
648       memset(entry->key, 0, entry->key_len / 8);
649       silc_free(entry->key);
650     }
651     silc_free(entry->cipher);
652     if (entry->hmac)
653       silc_hmac_free(entry->hmac);
654     silc_free(entry->hmac_name);
655     silc_free(entry->rekey);
656
657     memset(entry, 'F', sizeof(*entry));
658     silc_free(entry);
659     return TRUE;
660   }
661
662   return FALSE;
663 }
664
665 /* Finds channel by channel name. Channel names are unique and they
666    are not case-sensitive. */
667
668 SilcChannelEntry
669 silc_idlist_find_channel_by_name(SilcIDList id_list, char *name,
670                                  SilcIDCacheEntry *ret_entry)
671 {
672   SilcIDCacheEntry id_cache = NULL;
673
674   SILC_LOG_DEBUG(("Channel by name %s", name));
675
676   if (!silc_idcache_find_by_name_one(id_list->channels, name, &id_cache))
677     return NULL;
678
679   if (ret_entry)
680     *ret_entry = id_cache;
681
682   SILC_LOG_DEBUG(("Found"));
683
684   return id_cache->context;
685 }
686
687 /* Finds channel by Channel ID. */
688
689 SilcChannelEntry
690 silc_idlist_find_channel_by_id(SilcIDList id_list, SilcChannelID *id,
691                                SilcIDCacheEntry *ret_entry)
692 {
693   SilcIDCacheEntry id_cache = NULL;
694   SilcChannelEntry channel;
695
696   if (!id)
697     return NULL;
698
699   SILC_LOG_DEBUG(("Channel ID (%s)",
700                   silc_id_render(id, SILC_ID_CHANNEL)));
701
702   if (!silc_idcache_find_by_id_one(id_list->channels, (void *)id, &id_cache))
703     return NULL;
704
705   channel = (SilcChannelEntry)id_cache->context;
706
707   if (ret_entry)
708     *ret_entry = id_cache;
709
710   SILC_LOG_DEBUG(("Found"));
711
712   return channel;
713 }
714
715 /* Replaces old Channel ID with new one. This is done when router forces
716    normal server to change Channel ID. */
717
718 SilcChannelEntry
719 silc_idlist_replace_channel_id(SilcIDList id_list, SilcChannelID *old_id,
720                                SilcChannelID *new_id)
721 {
722   SilcIDCacheEntry id_cache = NULL;
723   SilcChannelEntry channel;
724
725   if (!old_id || !new_id)
726     return NULL;
727
728   SILC_LOG_DEBUG(("Replacing Channel ID"));
729
730   if (!silc_idcache_find_by_id_one(id_list->channels, (void *)old_id, 
731                                    &id_cache))
732     return NULL;
733
734   channel = (SilcChannelEntry)id_cache->context;
735
736   /* Remove the old entry and add a new one */
737
738   silc_idcache_del_by_id(id_list->channels, (void *)channel->id);
739
740   silc_free(channel->id);
741   channel->id = new_id;
742
743   silc_idcache_add(id_list->channels, channel->channel_name, channel->id, 
744                    channel, 0, NULL);
745
746   SILC_LOG_DEBUG(("Replaced"));
747
748   return channel;
749 }
750
751 /* Returns channels from the ID list. If the `channel_id' is NULL then
752    all channels are returned. */
753
754 SilcChannelEntry *
755 silc_idlist_get_channels(SilcIDList id_list, SilcChannelID *channel_id,
756                          SilcUInt32 *channels_count)
757 {
758   SilcIDCacheList list = NULL;
759   SilcIDCacheEntry id_cache = NULL;
760   SilcChannelEntry *channels = NULL;
761   int i = 0;
762
763   SILC_LOG_DEBUG(("Start"));
764
765   if (!channel_id) {
766     if (!silc_idcache_get_all(id_list->channels, &list))
767       return NULL;
768
769     channels = silc_calloc(silc_idcache_list_count(list), sizeof(*channels));
770     
771     i = 0;
772     silc_idcache_list_first(list, &id_cache);
773     channels[i++] = (SilcChannelEntry)id_cache->context;
774     
775     while (silc_idcache_list_next(list, &id_cache))
776       channels[i++] = (SilcChannelEntry)id_cache->context;
777     
778     silc_idcache_list_free(list);
779   } else {
780     if (!silc_idcache_find_by_id_one(id_list->channels, channel_id, &id_cache))
781       return NULL;
782
783     i = 1;
784     channels = silc_calloc(1, sizeof(*channels));
785     channels[0] = (SilcChannelEntry)id_cache->context;
786   }
787
788   if (channels_count)
789     *channels_count = i;
790
791   return channels;
792 }