6a4d5f767068236bdd717a67a06bbd02beccebcf
[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_list(entry->invite_list, &htl);
681       while (silc_hash_table_get(&htl, (void **)&type, (void **)&tmp)) {
682         if (type == 1) {
683           silc_free((char *)tmp);
684           continue;
685         }
686         silc_buffer_free(tmp);
687       }
688       silc_hash_table_list_reset(&htl);
689       silc_hash_table_free(entry->invite_list);
690     }
691
692     if (entry->ban_list) {
693       silc_hash_table_list(entry->ban_list, &htl);
694       while (silc_hash_table_get(&htl, (void **)&type, (void **)&tmp)) {
695         if (type == 1) {
696           silc_free((char *)tmp);
697           continue;
698         }
699         silc_buffer_free(tmp);
700       }
701       silc_hash_table_list_reset(&htl);
702       silc_hash_table_free(entry->ban_list);
703     }
704
705     if (entry->channel_key)
706       silc_cipher_free(entry->channel_key);
707     if (entry->key) {
708       memset(entry->key, 0, entry->key_len / 8);
709       silc_free(entry->key);
710     }
711     silc_free(entry->cipher);
712     if (entry->hmac)
713       silc_hmac_free(entry->hmac);
714     silc_free(entry->hmac_name);
715     silc_free(entry->rekey);
716     if (entry->founder_key)
717       silc_pkcs_public_key_free(entry->founder_key);
718
719     memset(entry, 'F', sizeof(*entry));
720     silc_free(entry);
721     return TRUE;
722   }
723
724   return FALSE;
725 }
726
727 /* Finds channel by channel name. Channel names are unique and they
728    are not case-sensitive. */
729
730 SilcChannelEntry
731 silc_idlist_find_channel_by_name(SilcIDList id_list, char *name,
732                                  SilcIDCacheEntry *ret_entry)
733 {
734   SilcIDCacheEntry id_cache = NULL;
735
736   SILC_LOG_DEBUG(("Channel by name %s", name));
737
738   if (!silc_idcache_find_by_name_one(id_list->channels, name, &id_cache))
739     return NULL;
740
741   if (ret_entry)
742     *ret_entry = id_cache;
743
744   SILC_LOG_DEBUG(("Found"));
745
746   /* Touch channel */
747   ((SilcChannelEntry)id_cache->context)->updated = time(NULL);
748
749   return id_cache->context;
750 }
751
752 /* Finds channel by Channel ID. */
753
754 SilcChannelEntry
755 silc_idlist_find_channel_by_id(SilcIDList id_list, SilcChannelID *id,
756                                SilcIDCacheEntry *ret_entry)
757 {
758   SilcIDCacheEntry id_cache = NULL;
759   SilcChannelEntry channel;
760
761   if (!id)
762     return NULL;
763
764   SILC_LOG_DEBUG(("Channel ID (%s)",
765                   silc_id_render(id, SILC_ID_CHANNEL)));
766
767   if (!silc_idcache_find_by_id_one(id_list->channels, (void *)id, &id_cache))
768     return NULL;
769
770   channel = (SilcChannelEntry)id_cache->context;
771
772   if (ret_entry)
773     *ret_entry = id_cache;
774
775   SILC_LOG_DEBUG(("Found"));
776
777   /* Touch channel */
778   channel->updated = time(NULL);
779
780   return channel;
781 }
782
783 /* Replaces old Channel ID with new one. This is done when router forces
784    normal server to change Channel ID. */
785
786 SilcChannelEntry
787 silc_idlist_replace_channel_id(SilcIDList id_list, SilcChannelID *old_id,
788                                SilcChannelID *new_id)
789 {
790   SilcIDCacheEntry id_cache = NULL;
791   SilcChannelEntry channel;
792
793   if (!old_id || !new_id)
794     return NULL;
795
796   SILC_LOG_DEBUG(("Replacing Channel ID"));
797
798   if (!silc_idcache_find_by_id_one(id_list->channels, (void *)old_id, 
799                                    &id_cache))
800     return NULL;
801
802   channel = (SilcChannelEntry)id_cache->context;
803
804   /* Remove the old entry and add a new one */
805
806   silc_idcache_del_by_id(id_list->channels, (void *)channel->id);
807
808   silc_free(channel->id);
809   channel->id = new_id;
810
811   silc_idcache_add(id_list->channels, channel->channel_name, channel->id, 
812                    channel, 0, NULL);
813
814   SILC_LOG_DEBUG(("Replaced"));
815
816   /* Touch channel */
817   channel->updated = time(NULL);
818
819   return channel;
820 }
821
822 /* Returns channels from the ID list. If the `channel_id' is NULL then
823    all channels are returned. */
824
825 SilcChannelEntry *
826 silc_idlist_get_channels(SilcIDList id_list, SilcChannelID *channel_id,
827                          SilcUInt32 *channels_count)
828 {
829   SilcIDCacheList list = NULL;
830   SilcIDCacheEntry id_cache = NULL;
831   SilcChannelEntry *channels = NULL;
832   int i = 0;
833
834   SILC_LOG_DEBUG(("Start"));
835
836   if (!channel_id) {
837     if (!silc_idcache_get_all(id_list->channels, &list))
838       return NULL;
839
840     channels = silc_calloc(silc_idcache_list_count(list), sizeof(*channels));
841     
842     i = 0;
843     silc_idcache_list_first(list, &id_cache);
844     channels[i++] = (SilcChannelEntry)id_cache->context;
845     
846     while (silc_idcache_list_next(list, &id_cache))
847       channels[i++] = (SilcChannelEntry)id_cache->context;
848     
849     silc_idcache_list_free(list);
850   } else {
851     if (!silc_idcache_find_by_id_one(id_list->channels, channel_id, &id_cache))
852       return NULL;
853
854     i = 1;
855     channels = silc_calloc(1, sizeof(*channels));
856     channels[0] = (SilcChannelEntry)id_cache->context;
857   }
858
859   if (channels_count)
860     *channels_count = i;
861
862   return channels;
863 }