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