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