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