Fixed socket connection counting. Added checks for not adding
[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(("Purging cache"));
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   if (entry) {
294     /* Remove from cache */
295     if (!silc_idcache_del_by_context(id_list->servers, entry))
296       return FALSE;
297
298     SILC_LOG_DEBUG(("Deleting server %s", entry->server_name ?
299                     entry->server_name : ""));
300
301     /* Free data */
302     silc_free(entry->server_name);
303     silc_free(entry->id);
304     silc_free(entry->server_info);
305
306     memset(entry, 'F', sizeof(*entry));
307     silc_free(entry);
308     return TRUE;
309   }
310
311   return FALSE;
312 }
313
314 /******************************************************************************
315
316                           Client entry functions
317
318 ******************************************************************************/
319
320 /* Add new client entry. This adds the client entry to ID cache system
321    and returns the allocated client entry or NULL on error.  This is
322    called when new client connection is accepted to the server. If The
323    `router' is provided then the all server routines assume that the client
324    is not directly connected local client but it has router set and is
325    remote.  If this is the case then `connection' must be NULL.  If, on the
326    other hand, the `connection' is provided then the client is assumed
327    to be directly connected local client and `router' must be NULL. */
328
329 SilcClientEntry
330 silc_idlist_add_client(SilcIDList id_list, char *nickname, char *username, 
331                        char *userinfo, SilcClientID *id, 
332                        SilcServerEntry router, void *connection,
333                        int expire)
334 {
335   SilcClientEntry client;
336
337   SILC_LOG_DEBUG(("Adding new client entry"));
338
339   client = silc_calloc(1, sizeof(*client));
340   client->nickname = nickname;
341   client->username = username;
342   client->userinfo = userinfo;
343   client->id = id;
344   client->router = router;
345   client->connection = connection;
346   client->channels = silc_hash_table_alloc(3, silc_hash_ptr, NULL,
347                                            NULL, NULL, NULL, NULL, TRUE);
348
349   if (!silc_idcache_add(id_list->clients, nickname, (void *)client->id, 
350                         (void *)client, expire, NULL)) {
351     silc_hash_table_free(client->channels);
352     silc_free(client);
353     return NULL;
354   }
355
356   return client;
357 }
358
359 /* Free client entry. This free's everything and removes the entry
360    from ID cache. Call silc_idlist_del_data before calling this one. */
361
362 int silc_idlist_del_client(SilcIDList id_list, SilcClientEntry entry)
363 {
364   SILC_LOG_DEBUG(("Start"));
365
366   if (entry) {
367     /* Remove from cache */
368     if (!silc_idcache_del_by_context(id_list->clients, entry))
369       return FALSE;
370
371     assert(!silc_hash_table_count(entry->channels));
372
373     /* Free data */
374     silc_free(entry->nickname);
375     silc_free(entry->servername);
376     silc_free(entry->username);
377     silc_free(entry->userinfo);
378     silc_free(entry->id);
379     silc_hash_table_free(entry->channels);
380
381     memset(entry, 'F', sizeof(*entry));
382     silc_free(entry);
383
384     return TRUE;
385   }
386
387   return FALSE;
388 }
389
390 /* Returns all clients matching requested nickname. Number of clients is
391    returned to `clients_count'. Caller must free the returned table. */
392
393 int silc_idlist_get_clients_by_nickname(SilcIDList id_list, char *nickname,
394                                         char *server, 
395                                         SilcClientEntry **clients,
396                                         SilcUInt32 *clients_count)
397 {
398   SilcIDCacheList list = NULL;
399   SilcIDCacheEntry id_cache = NULL;
400
401   SILC_LOG_DEBUG(("Start"));
402
403   if (!silc_idcache_find_by_name(id_list->clients, nickname, &list))
404     return FALSE;
405
406   *clients = silc_realloc(*clients, 
407                           (silc_idcache_list_count(list) + *clients_count) * 
408                           sizeof(**clients));
409
410   silc_idcache_list_first(list, &id_cache);
411   (*clients)[(*clients_count)++] = (SilcClientEntry)id_cache->context;
412
413   while (silc_idcache_list_next(list, &id_cache))
414     (*clients)[(*clients_count)++] = (SilcClientEntry)id_cache->context;
415   
416   silc_idcache_list_free(list);
417   
418   SILC_LOG_DEBUG(("Found total %d clients", *clients_count));
419
420   return TRUE;
421 }
422
423 /* Returns all clients matching requested nickname hash. Number of clients
424    is returned to `clients_count'. Caller must free the returned table. */
425
426 int silc_idlist_get_clients_by_hash(SilcIDList id_list, char *nickname,
427                                     SilcHash md5hash,
428                                     SilcClientEntry **clients,
429                                     SilcUInt32 *clients_count)
430 {
431   SilcIDCacheList list = NULL;
432   SilcIDCacheEntry id_cache = NULL;
433   unsigned char hash[32];
434   SilcClientID client_id;
435   char nick[128 + 1];
436
437   SILC_LOG_DEBUG(("Start"));
438
439   memset(nick, 0, sizeof(nick));
440   silc_to_lower(nickname, nick, sizeof(nick) - 1);
441   silc_hash_make(md5hash, nick, strlen(nick), hash);
442
443   /* As the Client ID is hashed in the ID cache by hashing only the hash
444      from the Client ID, we can do a lookup with only the hash not the
445      other parts of the ID and get all the clients with that hash, ie.
446      with that nickname, as the hash is from the nickname. */
447   memset(&client_id, 0, sizeof(client_id));
448   memcpy(&client_id.hash, hash, sizeof(client_id.hash));
449   if (!silc_idcache_find_by_id(id_list->clients, &client_id, &list))
450     return FALSE;
451
452   *clients = silc_realloc(*clients, 
453                           (silc_idcache_list_count(list) + *clients_count) * 
454                           sizeof(**clients));
455
456   silc_idcache_list_first(list, &id_cache);
457   (*clients)[(*clients_count)++] = (SilcClientEntry)id_cache->context;
458
459   while (silc_idcache_list_next(list, &id_cache))
460     (*clients)[(*clients_count)++] = (SilcClientEntry)id_cache->context;
461   
462   silc_idcache_list_free(list);
463   
464   SILC_LOG_DEBUG(("Found total %d clients", *clients_count));
465
466   return TRUE;
467 }
468
469 /* Finds client by Client ID */
470
471 SilcClientEntry
472 silc_idlist_find_client_by_id(SilcIDList id_list, SilcClientID *id,
473                               bool registered, SilcIDCacheEntry *ret_entry)
474 {
475   SilcIDCacheEntry id_cache = NULL;
476   SilcClientEntry client;
477
478   if (!id)
479     return NULL;
480
481   SILC_LOG_DEBUG(("Client ID (%s)", 
482                   silc_id_render(id, SILC_ID_CLIENT)));
483
484   /* Do extended search since the normal ID comparison function for
485      Client ID's compares only the hash from the Client ID and not the
486      entire ID. The silc_hash_client_id_compare compares the entire
487      Client ID as we want to find one specific Client ID. */
488   if (!silc_idcache_find_by_id_one_ext(id_list->clients, (void *)id, 
489                                        NULL, NULL, 
490                                        silc_hash_client_id_compare, NULL,
491                                        &id_cache))
492     return NULL;
493
494   client = (SilcClientEntry)id_cache->context;
495
496   if (ret_entry)
497     *ret_entry = id_cache;
498
499   if (client && registered &&
500       !(client->data.status & SILC_IDLIST_STATUS_REGISTERED))
501     return NULL;
502
503   SILC_LOG_DEBUG(("Found"));
504
505   return client;
506 }
507
508 /* Replaces old Client ID with new one */
509
510 SilcClientEntry
511 silc_idlist_replace_client_id(SilcServer server,
512                               SilcIDList id_list, SilcClientID *old_id,
513                               SilcClientID *new_id, const char *nickname)
514 {
515   SilcIDCacheEntry id_cache = NULL;
516   SilcClientEntry client;
517
518   if (!old_id || !new_id)
519     return NULL;
520
521   SILC_LOG_DEBUG(("Replacing Client ID"));
522
523   /* Do extended search since the normal ID comparison function for
524      Client ID's compares only the hash from the Client ID and not the
525      entire ID. The silc_hash_client_id_compare compares the entire
526      Client ID as we want to find one specific Client ID. */
527   if (!silc_idcache_find_by_id_one_ext(id_list->clients, (void *)old_id, 
528                                        NULL, NULL, 
529                                        silc_hash_client_id_compare, NULL,
530                                        &id_cache))
531     return NULL;
532
533   client = (SilcClientEntry)id_cache->context;
534
535   /* Remove the old entry and add a new one */
536
537   if (!silc_idcache_del_by_context(id_list->clients, client))
538     return NULL;
539
540   /* Check if anyone is watching old nickname */
541   if (server->server_type == SILC_ROUTER)
542     silc_server_check_watcher_list(server, client, nickname,
543                                    SILC_NOTIFY_TYPE_NICK_CHANGE);
544
545   silc_free(client->id);
546   silc_free(client->nickname);
547   client->id = new_id;
548   client->nickname = nickname ? strdup(nickname) : NULL;
549
550   /* Check if anyone is watching new nickname */
551   if (server->server_type == SILC_ROUTER)
552     silc_server_check_watcher_list(server, client, nickname,
553                                    SILC_NOTIFY_TYPE_NICK_CHANGE);
554
555   if (!silc_idcache_add(id_list->clients, client->nickname, client->id, 
556                         client, 0, NULL))
557     return NULL;
558
559   SILC_LOG_DEBUG(("Replaced"));
560
561   return client;
562 }
563
564 /* Client cache entry destructor that is called when the cache is purged. */
565
566 void silc_idlist_client_destructor(SilcIDCache cache,
567                                    SilcIDCacheEntry entry)
568 {
569   SilcClientEntry client;
570
571   client = (SilcClientEntry)entry->context;
572   if (client) {
573     silc_free(client->nickname);
574     silc_free(client->username);
575     silc_free(client->userinfo);
576     silc_free(client->id);
577     silc_hash_table_free(client->channels);
578
579     memset(client, 'A', sizeof(*client));
580     silc_free(client);
581   }
582 }
583
584 /******************************************************************************
585
586                           Channel entry functions
587
588 ******************************************************************************/
589
590 /* Add new channel entry. This add the new channel entry to the ID cache
591    system and returns the allocated entry or NULL on error. */
592
593 SilcChannelEntry
594 silc_idlist_add_channel(SilcIDList id_list, char *channel_name, int mode,
595                         SilcChannelID *id, SilcServerEntry router,
596                         SilcCipher channel_key, SilcHmac hmac,
597                         int expire)
598 {
599   SilcChannelEntry channel;
600
601   SILC_LOG_DEBUG(("Adding new channel %s", channel_name));
602
603   channel = silc_calloc(1, sizeof(*channel));
604   channel->channel_name = channel_name;
605   channel->mode = mode;
606   channel->id = id;
607   channel->router = router;
608   channel->channel_key = channel_key;
609   channel->hmac = hmac;
610   channel->created = channel->updated = time(0);
611   if (!channel->hmac)
612     if (!silc_hmac_alloc(SILC_DEFAULT_HMAC, NULL, &channel->hmac)) {
613       silc_free(channel);
614       return NULL;
615     }
616
617   channel->user_list = silc_hash_table_alloc(3, silc_hash_ptr, NULL, NULL,
618                                              NULL, NULL, NULL, TRUE);
619
620   if (!silc_idcache_add(id_list->channels, channel->channel_name, 
621                         (void *)channel->id, (void *)channel, expire, NULL)) {
622     silc_hmac_free(channel->hmac);
623     silc_hash_table_free(channel->user_list);
624     silc_free(channel);
625     return NULL;
626   }
627
628   return channel;
629 }
630
631 /* Foreach callbcak to free all users from the channel when deleting a
632    channel entry. */
633
634 static void silc_idlist_del_channel_foreach(void *key, void *context,
635                                             void *user_context)
636 {
637   SilcChannelClientEntry chl = (SilcChannelClientEntry)context;
638
639   SILC_LOG_DEBUG(("Removing client %s from channel %s",
640                   chl->client->nickname ? chl->client->nickname :
641                   (unsigned char *)"", chl->channel->channel_name));
642
643   /* Remove the context from the client's channel hash table as that
644      table and channel's user_list hash table share this same context. */
645   silc_hash_table_del(chl->client->channels, chl->channel);
646   silc_free(chl);
647 }
648
649 /* Free channel entry.  This free's everything. */
650
651 int silc_idlist_del_channel(SilcIDList id_list, SilcChannelEntry entry)
652 {
653   if (entry) {
654     /* Remove from cache */
655     if (!silc_idcache_del_by_context(id_list->channels, entry))
656       return FALSE;
657
658     SILC_LOG_DEBUG(("Deleting channel %s", entry->channel_name));
659
660     /* Free all client entrys from the users list. The silc_hash_table_free
661        will free all the entries so they are not freed at the foreach 
662        callback. */
663     silc_hash_table_foreach(entry->user_list, silc_idlist_del_channel_foreach,
664                             NULL);
665     silc_hash_table_free(entry->user_list);
666
667     /* Free data */
668     silc_free(entry->channel_name);
669     silc_free(entry->id);
670     silc_free(entry->topic);
671     if (entry->channel_key)
672       silc_cipher_free(entry->channel_key);
673     if (entry->key) {
674       memset(entry->key, 0, entry->key_len / 8);
675       silc_free(entry->key);
676     }
677     silc_free(entry->cipher);
678     if (entry->hmac)
679       silc_hmac_free(entry->hmac);
680     silc_free(entry->hmac_name);
681     silc_free(entry->rekey);
682     if (entry->founder_key)
683       silc_pkcs_public_key_free(entry->founder_key);
684
685     memset(entry, 'F', sizeof(*entry));
686     silc_free(entry);
687     return TRUE;
688   }
689
690   return FALSE;
691 }
692
693 /* Finds channel by channel name. Channel names are unique and they
694    are not case-sensitive. */
695
696 SilcChannelEntry
697 silc_idlist_find_channel_by_name(SilcIDList id_list, char *name,
698                                  SilcIDCacheEntry *ret_entry)
699 {
700   SilcIDCacheEntry id_cache = NULL;
701
702   SILC_LOG_DEBUG(("Channel by name %s", name));
703
704   if (!silc_idcache_find_by_name_one(id_list->channels, name, &id_cache))
705     return NULL;
706
707   if (ret_entry)
708     *ret_entry = id_cache;
709
710   SILC_LOG_DEBUG(("Found"));
711
712   /* Touch channel */
713   ((SilcChannelEntry)id_cache->context)->updated = time(NULL);
714
715   return id_cache->context;
716 }
717
718 /* Finds channel by Channel ID. */
719
720 SilcChannelEntry
721 silc_idlist_find_channel_by_id(SilcIDList id_list, SilcChannelID *id,
722                                SilcIDCacheEntry *ret_entry)
723 {
724   SilcIDCacheEntry id_cache = NULL;
725   SilcChannelEntry channel;
726
727   if (!id)
728     return NULL;
729
730   SILC_LOG_DEBUG(("Channel ID (%s)",
731                   silc_id_render(id, SILC_ID_CHANNEL)));
732
733   if (!silc_idcache_find_by_id_one(id_list->channels, (void *)id, &id_cache))
734     return NULL;
735
736   channel = (SilcChannelEntry)id_cache->context;
737
738   if (ret_entry)
739     *ret_entry = id_cache;
740
741   SILC_LOG_DEBUG(("Found"));
742
743   /* Touch channel */
744   channel->updated = time(NULL);
745
746   return channel;
747 }
748
749 /* Replaces old Channel ID with new one. This is done when router forces
750    normal server to change Channel ID. */
751
752 SilcChannelEntry
753 silc_idlist_replace_channel_id(SilcIDList id_list, SilcChannelID *old_id,
754                                SilcChannelID *new_id)
755 {
756   SilcIDCacheEntry id_cache = NULL;
757   SilcChannelEntry channel;
758
759   if (!old_id || !new_id)
760     return NULL;
761
762   SILC_LOG_DEBUG(("Replacing Channel ID"));
763
764   if (!silc_idcache_find_by_id_one(id_list->channels, (void *)old_id, 
765                                    &id_cache))
766     return NULL;
767
768   channel = (SilcChannelEntry)id_cache->context;
769
770   /* Remove the old entry and add a new one */
771
772   silc_idcache_del_by_id(id_list->channels, (void *)channel->id);
773
774   silc_free(channel->id);
775   channel->id = new_id;
776
777   silc_idcache_add(id_list->channels, channel->channel_name, channel->id, 
778                    channel, 0, NULL);
779
780   SILC_LOG_DEBUG(("Replaced"));
781
782   /* Touch channel */
783   channel->updated = time(NULL);
784
785   return channel;
786 }
787
788 /* Returns channels from the ID list. If the `channel_id' is NULL then
789    all channels are returned. */
790
791 SilcChannelEntry *
792 silc_idlist_get_channels(SilcIDList id_list, SilcChannelID *channel_id,
793                          SilcUInt32 *channels_count)
794 {
795   SilcIDCacheList list = NULL;
796   SilcIDCacheEntry id_cache = NULL;
797   SilcChannelEntry *channels = NULL;
798   int i = 0;
799
800   SILC_LOG_DEBUG(("Start"));
801
802   if (!channel_id) {
803     if (!silc_idcache_get_all(id_list->channels, &list))
804       return NULL;
805
806     channels = silc_calloc(silc_idcache_list_count(list), sizeof(*channels));
807     
808     i = 0;
809     silc_idcache_list_first(list, &id_cache);
810     channels[i++] = (SilcChannelEntry)id_cache->context;
811     
812     while (silc_idcache_list_next(list, &id_cache))
813       channels[i++] = (SilcChannelEntry)id_cache->context;
814     
815     silc_idcache_list_free(list);
816   } else {
817     if (!silc_idcache_find_by_id_one(id_list->channels, channel_id, &id_cache))
818       return NULL;
819
820     i = 1;
821     channels = silc_calloc(1, sizeof(*channels));
822     channels[0] = (SilcChannelEntry)id_cache->context;
823   }
824
825   if (channels_count)
826     *channels_count = i;
827
828   return channels;
829 }