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