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