silcd: fixed memory leaks
[silc.git] / apps / silcd / idlist.c
1 /*
2
3   idlist.c
4
5   Author: Pekka Riikonen <priikone@silcnet.org>
6
7   Copyright (C) 1997 - 2007 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 = entry;
38   data->conn_type = idata->conn_type;
39   data->sconn = idata->sconn;
40   data->hash = idata->hash;
41   data->public_key = idata->public_key;
42   memcpy(data->fingerprint, idata->fingerprint, sizeof(data->fingerprint));
43   data->rekey = idata->rekey;
44   data->last_receive = idata->last_receive;
45   data->last_sent = idata->last_sent;
46   data->status = idata->status;
47   data->created = time(0);      /* Update creation time */
48 }
49
50 /* Free's all data in the common ID entry data structure. */
51
52 void silc_idlist_del_data(void *entry)
53 {
54   SilcIDListData idata = (SilcIDListData)entry;
55
56   if (idata->hash)
57     silc_hash_free(idata->hash);
58   if (idata->rekey)
59     silc_ske_free_rekey_material(idata->rekey);
60
61   idata->rekey = NULL;
62   idata->hash = NULL;
63   idata->public_key = NULL;
64 }
65
66 /******************************************************************************
67
68                           Server entry functions
69
70 ******************************************************************************/
71
72 /* Add new server entry. This adds the new server entry to ID cache and
73    returns the allocated entry object or NULL on error. This is called
74    when new server connects to us. We also add ourselves to cache with
75    this function. */
76
77 SilcServerEntry
78 silc_idlist_add_server(SilcIDList id_list,
79                        char *server_name, int server_type,
80                        SilcServerID *id, SilcServerEntry router,
81                        void *connection)
82 {
83   SilcServerEntry server;
84   char *server_namec = NULL;
85
86   SILC_LOG_DEBUG(("Adding new server entry"));
87
88   /* Normalize name.  This is cached, original is in server context.  */
89   if (server_name) {
90     server_namec = silc_identifier_check(server_name, strlen(server_name),
91                                          SILC_STRING_UTF8, 256, NULL);
92     if (!server_namec)
93       return NULL;
94   }
95
96   server = silc_calloc(1, sizeof(*server));
97   server->server_name = server_name;
98   server->server_type = server_type;
99   server->id = id;
100   server->router = router;
101   server->connection = connection;
102
103   if (!silc_idcache_add(id_list->servers, server_namec,
104                         (void *)server->id, (void *)server)) {
105     silc_free(server);
106     silc_free(server_namec);
107     return NULL;
108   }
109
110   return server;
111 }
112
113 /* Finds server by Server ID */
114
115 SilcServerEntry
116 silc_idlist_find_server_by_id(SilcIDList id_list, SilcServerID *id,
117                               SilcBool registered, SilcIDCacheEntry *ret_entry)
118 {
119   SilcIDCacheEntry id_cache = NULL;
120   SilcServerEntry server;
121
122   if (!id)
123     return NULL;
124
125   SILC_LOG_DEBUG(("Server ID (%s)",
126                   silc_id_render(id, SILC_ID_SERVER)));
127
128   if (!silc_idcache_find_by_id_one(id_list->servers, (void *)id, &id_cache))
129     return NULL;
130
131   server = (SilcServerEntry)id_cache->context;
132
133   if (server && registered &&
134       !(server->data.status & SILC_IDLIST_STATUS_REGISTERED))
135     return NULL;
136
137   if (ret_entry)
138     *ret_entry = id_cache;
139
140   SILC_LOG_DEBUG(("Found"));
141
142   return server;
143 }
144
145 /* Find server by name.  The 'name' must be normalized already. */
146
147 SilcServerEntry
148 silc_idlist_find_server_by_name(SilcIDList id_list, char *name,
149                                 SilcBool registered,
150                                 SilcIDCacheEntry *ret_entry)
151 {
152   SilcIDCacheEntry id_cache = NULL;
153   SilcServerEntry server;
154
155   SILC_LOG_DEBUG(("Server by name `%s'", name));
156
157   if (!silc_idcache_find_by_name_one(id_list->servers, name, &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 connection parameters, hostname and port */
175
176 SilcServerEntry
177 silc_idlist_find_server_by_conn(SilcIDList id_list, char *hostname,
178                                 int port, SilcBool registered,
179                                 SilcIDCacheEntry *ret_entry)
180 {
181   SilcList list;
182   SilcIDCacheEntry id_cache = NULL;
183   SilcServerEntry server = NULL;
184   SilcPacketStream sock;
185   const char *host = NULL, *ip = NULL;
186
187   SILC_LOG_DEBUG(("Server by hostname %s and port %d", hostname, port));
188
189   if (!silc_idcache_get_all(id_list->servers, &list))
190     return NULL;
191
192   silc_list_start(list);
193   while ((id_cache = silc_list_get(list))) {
194     server = id_cache->context;
195     sock = server->connection;
196
197     if (sock && silc_socket_stream_get_info(
198                             silc_packet_stream_get_stream(sock),
199                             NULL, &host, &ip, NULL)) {
200       if (((host && !strcasecmp(host, hostname)) ||
201            (ip && !strcasecmp(ip, hostname))) &&
202           server->id->port == SILC_SWAB_16(port))
203         break;
204     }
205
206     id_cache = NULL;
207     server = NULL;
208   }
209
210   if (server && registered &&
211       !(server->data.status & SILC_IDLIST_STATUS_REGISTERED))
212     return NULL;
213
214   if (ret_entry)
215     *ret_entry = id_cache;
216
217   SILC_LOG_DEBUG(("Found"));
218
219   return server;
220 }
221
222 /* Replaces old Server ID with new one */
223
224 SilcServerEntry
225 silc_idlist_replace_server_id(SilcIDList id_list, SilcServerID *old_id,
226                               SilcServerID *new_id)
227 {
228   SilcIDCacheEntry id_cache = NULL;
229   SilcServerEntry server;
230   char *name;
231
232   if (!old_id || !new_id)
233     return NULL;
234
235   SILC_LOG_DEBUG(("Replacing Server ID"));
236
237   if (!silc_idcache_find_by_id_one(id_list->servers, (void *)old_id, &id_cache))
238     return NULL;
239
240   server = (SilcServerEntry)id_cache->context;
241   name = strdup(id_cache->name);
242
243   /* Remove the old entry and add a new one */
244
245   silc_idcache_del_by_id(id_list->servers, (void *)server->id, NULL);
246   *server->id = *new_id;
247   silc_idcache_add(id_list->servers, name, server->id, server);
248
249   SILC_LOG_DEBUG(("Found"));
250
251   return server;
252 }
253
254 /* Removes and free's server entry from ID list */
255
256 int silc_idlist_del_server(SilcIDList id_list, SilcServerEntry entry)
257 {
258   if (entry) {
259     /* Remove from cache */
260     if (!silc_idcache_del_by_context(id_list->servers, entry, NULL)) {
261       SILC_LOG_DEBUG(("Unknown server, did not delete"));
262       return FALSE;
263     }
264
265     SILC_LOG_DEBUG(("Deleting server %s id %s", entry->server_name ?
266                     entry->server_name : "",
267                     entry->id ?
268                     silc_id_render(entry->id, SILC_ID_SERVER) : ""));
269
270     /* Free data */
271     silc_free(entry->server_name);
272     silc_free(entry->id);
273     silc_free(entry->server_info);
274
275     memset(entry, 'F', sizeof(*entry));
276     silc_free(entry);
277     return TRUE;
278   }
279
280   return FALSE;
281 }
282
283 /* ID Cache destructor */
284
285 void silc_idlist_server_destructor(SilcIDCache cache,
286                                    SilcIDCacheEntry entry,
287                                    void *dest_context,
288                                    void *app_context)
289 {
290   silc_free(entry->name);
291 }
292
293 /******************************************************************************
294
295                           Client entry functions
296
297 ******************************************************************************/
298
299 /* Add new client entry. This adds the client entry to ID cache system
300    and returns the allocated client entry or NULL on error.  This is
301    called when new client connection is accepted to the server. If The
302    `router' is provided then the all server routines assume that the client
303    is not directly connected local client but it has router set and is
304    remote.  If this is the case then `connection' must be NULL.  If, on the
305    other hand, the `connection' is provided then the client is assumed
306    to be directly connected local client and `router' must be NULL. */
307
308 SilcClientEntry
309 silc_idlist_add_client(SilcIDList id_list, char *nickname, char *username,
310                        char *userinfo, SilcClientID *id,
311                        SilcServerEntry router, void *connection)
312 {
313   SilcClientEntry client;
314   char *nicknamec = NULL;
315
316   SILC_LOG_DEBUG(("Adding new client entry"));
317
318   /* Normalize name.  This is cached, original is in client context.  */
319   if (nickname) {
320     nicknamec = silc_identifier_check(nickname, strlen(nickname),
321                                       SILC_STRING_UTF8, 128, NULL);
322     if (!nicknamec)
323       return NULL;
324   }
325
326   /* Check username. */
327   if (username) {
328     char u[128 + 1], h[256 + 1];
329     int ret;
330
331     ret = silc_parse_userfqdn(username, u, sizeof(u), h, sizeof(h));
332     if (!ret)
333       return NULL;
334     if (!silc_identifier_verify(u, strlen(u), SILC_STRING_UTF8, 128))
335       return NULL;
336     if (ret > 1 && !silc_identifier_verify(h, strlen(h),
337                                            SILC_STRING_UTF8, 256))
338       return NULL;
339   }
340
341   client = silc_calloc(1, sizeof(*client));
342   if (!client)
343     return NULL;
344   client->nickname = nickname;
345   client->username = username ? strdup(username) : NULL;
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, nicknamec, (void *)client->id,
354                         (void *)client)) {
355     silc_hash_table_free(client->channels);
356     silc_free(client);
357     silc_free(nicknamec);
358     return NULL;
359   }
360
361   return client;
362 }
363
364 /* Free client entry. This free's everything and removes the entry
365    from ID cache. Call silc_idlist_del_data before calling this one. */
366
367 int silc_idlist_del_client(SilcIDList id_list, SilcClientEntry entry)
368 {
369   SILC_LOG_DEBUG(("Delete client %p", entry));
370
371   if (entry) {
372     /* Delete client, destructor will free data */
373     if (!silc_idcache_del_by_context(id_list->clients, entry, NULL)) {
374       SILC_LOG_DEBUG(("Unknown client, did not delete"));
375       return FALSE;
376     }
377     return TRUE;
378   }
379
380   return FALSE;
381 }
382
383 /* ID Cache destructor */
384
385 void silc_idlist_client_destructor(SilcIDCache cache,
386                                    SilcIDCacheEntry entry,
387                                    void *dest_context,
388                                    void *app_context)
389 {
390   SilcServer server = dest_context;
391   SilcClientEntry client;
392
393   client = (SilcClientEntry)entry->context;
394   if (client) {
395     /* Remove client's public key from repository, this will free it too. */
396     if (client->data.public_key)
397       silc_skr_del_public_key(server->repository, client->data.public_key,
398                               client);
399
400     assert(!silc_hash_table_count(client->channels));
401     silc_free(entry->name);
402     silc_free(client->nickname);
403     silc_free(client->servername);
404     silc_free(client->username);
405     silc_free(client->userinfo);
406     silc_free(client->id);
407     silc_free(client->attrs);
408     silc_hash_table_free(client->channels);
409
410     memset(client, 'A', sizeof(*client));
411     silc_free(client);
412   }
413 }
414
415 /* Returns all clients matching requested nickname. Number of clients is
416    returned to `clients_count'. Caller must free the returned table.
417    The 'nickname' must be normalized already. */
418
419 int silc_idlist_get_clients_by_nickname(SilcIDList id_list, char *nickname,
420                                         char *server,
421                                         SilcClientEntry **clients,
422                                         SilcUInt32 *clients_count)
423 {
424   SilcList list;
425   SilcIDCacheEntry id_cache = NULL;
426
427   SILC_LOG_DEBUG(("Start"));
428
429   if (!silc_idcache_find_by_name(id_list->clients, nickname, &list))
430     return FALSE;
431
432   *clients = silc_realloc(*clients,
433                           (silc_list_count(list) + *clients_count) *
434                           sizeof(**clients));
435
436   silc_list_start(list);
437   while ((id_cache = silc_list_get(list)))
438     (*clients)[(*clients_count)++] = id_cache->context;
439
440   SILC_LOG_DEBUG(("Found total %d clients", *clients_count));
441
442   return TRUE;
443 }
444
445 /* Returns all clients matching requested nickname hash. Number of clients
446    is returned to `clients_count'. Caller must free the returned table.
447    The 'nickname' must be normalized already. */
448
449 int silc_idlist_get_clients_by_hash(SilcIDList id_list,
450                                     char *nickname, char *server,
451                                     SilcHash md5hash,
452                                     SilcClientEntry **clients,
453                                     SilcUInt32 *clients_count)
454 {
455   SilcList list;
456   SilcIDCacheEntry id_cache = NULL;
457   unsigned char hash[SILC_HASH_MAXLEN];
458   SilcClientID client_id;
459   SilcClientEntry client_entry;
460
461   SILC_LOG_DEBUG(("Start"));
462
463   silc_hash_make(md5hash, nickname, strlen(nickname), hash);
464
465   /* As the Client ID is hashed in the ID cache by hashing only the hash
466      from the Client ID, we can do a lookup with only the hash not the
467      other parts of the ID and get all the clients with that hash, ie.
468      with that nickname, as the hash is from the nickname. */
469   memset(&client_id, 0, sizeof(client_id));
470   memcpy(&client_id.hash, hash, sizeof(client_id.hash));
471   if (!silc_idcache_find_by_id(id_list->clients, &client_id, &list))
472     return FALSE;
473
474   /* If server is specified, narrow the search with it. */
475   if (server) {
476     silc_list_start(list);
477     while ((id_cache = silc_list_get(list))) {
478       client_entry = id_cache->context;
479       if (!client_entry->servername)
480         continue;
481       if (!silc_utf8_strcasecmp(client_entry->servername, server))
482         silc_list_del(list, id_cache);
483     }
484   }
485
486   if (!silc_list_count(list))
487     return FALSE;
488
489   *clients = silc_realloc(*clients,
490                           (silc_list_count(list) + *clients_count) *
491                           sizeof(**clients));
492
493   silc_list_start(list);
494   while ((id_cache = silc_list_get(list)))
495     (*clients)[(*clients_count)++] = id_cache->context;
496
497   SILC_LOG_DEBUG(("Found total %d clients", *clients_count));
498
499   return TRUE;
500 }
501
502 /* Finds client by Client ID */
503
504 SilcClientEntry
505 silc_idlist_find_client_by_id(SilcIDList id_list, SilcClientID *id,
506                               SilcBool registered, SilcIDCacheEntry *ret_entry)
507 {
508   SilcIDCacheEntry id_cache = NULL;
509   SilcClientEntry client;
510
511   if (!id)
512     return NULL;
513
514   SILC_LOG_DEBUG(("Client ID (%s)",
515                   silc_id_render(id, SILC_ID_CLIENT)));
516
517   /* Find the exact client with the exact Client ID */
518   if (!silc_idcache_find_by_id_one(id_list->clients, (void *)id, &id_cache))
519     return NULL;
520
521   client = (SilcClientEntry)id_cache->context;
522
523   if (client && registered &&
524       !(client->data.status & SILC_IDLIST_STATUS_REGISTERED))
525     return NULL;
526
527   if (ret_entry)
528     *ret_entry = id_cache;
529
530   SILC_LOG_DEBUG(("Found"));
531
532   return client;
533 }
534
535 /* Replaces old Client ID with new one */
536
537 SilcClientEntry
538 silc_idlist_replace_client_id(SilcServer server,
539                               SilcIDList id_list, SilcClientID *old_id,
540                               SilcClientID *new_id, const char *nickname)
541 {
542   SilcIDCacheEntry id_cache = NULL;
543   SilcClientEntry client;
544   char *nicknamec = NULL;
545
546   if (!old_id || !new_id)
547     return NULL;
548
549   SILC_LOG_DEBUG(("Replacing Client ID"));
550
551   /* Normalize name. This is cached, original is in client context.  */
552   if (nickname) {
553     nicknamec = silc_identifier_check(nickname, strlen(nickname),
554                                       SILC_STRING_UTF8, 128, NULL);
555     if (!nicknamec)
556       return NULL;
557   }
558
559   /* Find exact client with exact Client ID */
560   if (!silc_idcache_find_by_id_one(id_list->clients, old_id, &id_cache))
561     return NULL;
562
563   client = (SilcClientEntry)id_cache->context;
564
565   /* Check if anyone is watching old nickname */
566   if (server->server_type == SILC_ROUTER)
567     silc_server_check_watcher_list(server, client, nickname,
568                                    SILC_NOTIFY_TYPE_NICK_CHANGE);
569
570   /* Replace */
571   if (!silc_idcache_update(id_list->clients, id_cache, new_id, nicknamec,
572                            TRUE))
573     return NULL;
574
575   silc_free(client->nickname);
576   client->nickname = nickname ? strdup(nickname) : NULL;
577
578   /* Check if anyone is watching new nickname */
579   if (server->server_type == SILC_ROUTER)
580     silc_server_check_watcher_list(server, client, nickname,
581                                    SILC_NOTIFY_TYPE_NICK_CHANGE);
582
583   SILC_LOG_DEBUG(("Replaced"));
584
585   return 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 send_key, SilcCipher receive_key,
602                         SilcHmac hmac)
603 {
604   SilcChannelEntry channel;
605   char *channel_namec = NULL;
606
607   SILC_LOG_DEBUG(("Adding new channel %s", channel_name));
608
609   /* Normalize name.  This is cached, original is in client context.  */
610   if (channel_name) {
611     channel_namec = silc_channel_name_check(channel_name, strlen(channel_name),
612                                             SILC_STRING_UTF8, 256, NULL);
613     if (!channel_namec)
614       return NULL;
615   }
616
617   channel = silc_calloc(1, sizeof(*channel));
618   channel->channel_name = channel_name;
619   channel->mode = mode;
620   channel->id = id;
621   channel->router = router;
622   channel->send_key = send_key;
623   channel->receive_key = receive_key;
624   channel->hmac = hmac;
625   channel->created = channel->updated = time(0);
626   if (!channel->hmac)
627     if (!silc_hmac_alloc(SILC_DEFAULT_HMAC, NULL, &channel->hmac)) {
628       silc_free(channel);
629       return NULL;
630     }
631
632   channel->user_list = silc_hash_table_alloc(3, silc_hash_ptr, NULL, NULL,
633                                              NULL, NULL, NULL, TRUE);
634
635   if (!silc_idcache_add(id_list->channels, channel_namec,
636                         (void *)channel->id, (void *)channel)) {
637     silc_hmac_free(channel->hmac);
638     silc_hash_table_free(channel->user_list);
639     silc_free(channel);
640     silc_free(channel_namec);
641     return NULL;
642   }
643
644   return channel;
645 }
646
647 /* ID Cache destructor */
648
649 void silc_idlist_channel_destructor(SilcIDCache cache,
650                                     SilcIDCacheEntry entry,
651                                     void *dest_context,
652                                     void *app_context)
653 {
654   silc_free(entry->name);
655 }
656
657 /* Foreach callbcak to free all users from the channel when deleting a
658    channel entry. */
659
660 static void silc_idlist_del_channel_foreach(void *key, void *context,
661                                             void *user_context)
662 {
663   SilcChannelClientEntry chl = (SilcChannelClientEntry)context;
664
665   SILC_LOG_DEBUG(("Removing client %s from channel %s",
666                   chl->client->nickname ? chl->client->nickname :
667                   (unsigned char *)"", chl->channel->channel_name));
668
669   /* Remove the context from the client's channel hash table as that
670      table and channel's user_list hash table share this same context. */
671   silc_hash_table_del(chl->client->channels, chl->channel);
672   silc_free(chl);
673 }
674
675 /* Free channel entry.  This free's everything. */
676
677 int silc_idlist_del_channel(SilcIDList id_list, SilcChannelEntry entry)
678 {
679   if (entry) {
680     /* Remove from cache */
681     if (!silc_idcache_del_by_context(id_list->channels, entry, NULL)) {
682       SILC_LOG_DEBUG(("Unknown channel, did not delete"));
683       return FALSE;
684     }
685
686     SILC_LOG_DEBUG(("Deleting channel %s", entry->channel_name));
687
688     /* Free all client entrys from the users list. The silc_hash_table_free
689        will free all the entries so they are not freed at the foreach
690        callback. */
691     silc_hash_table_foreach(entry->user_list, silc_idlist_del_channel_foreach,
692                             NULL);
693     silc_hash_table_free(entry->user_list);
694
695     /* Free data */
696     silc_free(entry->channel_name);
697     silc_free(entry->id);
698     silc_free(entry->topic);
699
700     if (entry->invite_list)
701       silc_hash_table_free(entry->invite_list);
702     if (entry->ban_list)
703       silc_hash_table_free(entry->ban_list);
704
705     if (entry->send_key)
706       silc_cipher_free(entry->send_key);
707     if (entry->receive_key)
708       silc_cipher_free(entry->receive_key);
709     if (entry->key) {
710       memset(entry->key, 0, entry->key_len / 8);
711       silc_free(entry->key);
712     }
713     silc_free(entry->cipher);
714     if (entry->hmac)
715       silc_hmac_free(entry->hmac);
716     silc_free(entry->hmac_name);
717     silc_free(entry->rekey);
718     if (entry->founder_key)
719       silc_pkcs_public_key_free(entry->founder_key);
720     if (entry->channel_pubkeys)
721       silc_hash_table_free(entry->channel_pubkeys);
722
723     memset(entry, 'F', sizeof(*entry));
724     silc_free(entry);
725     return TRUE;
726   }
727
728   return FALSE;
729 }
730
731 /* Finds channel by channel name. Channel names are unique and they
732    are not case-sensitive.  The 'name' must be normalized already. */
733
734 SilcChannelEntry
735 silc_idlist_find_channel_by_name(SilcIDList id_list, char *name,
736                                  SilcIDCacheEntry *ret_entry)
737 {
738   SilcIDCacheEntry id_cache = NULL;
739
740   SILC_LOG_DEBUG(("Channel by name %s", name));
741
742   if (!silc_idcache_find_by_name_one(id_list->channels, name, &id_cache))
743     return NULL;
744
745   if (ret_entry)
746     *ret_entry = id_cache;
747
748   SILC_LOG_DEBUG(("Found"));
749
750   /* Touch channel */
751   ((SilcChannelEntry)id_cache->context)->updated = time(NULL);
752
753   return id_cache->context;
754 }
755
756 /* Finds channel by Channel ID. */
757
758 SilcChannelEntry
759 silc_idlist_find_channel_by_id(SilcIDList id_list, SilcChannelID *id,
760                                SilcIDCacheEntry *ret_entry)
761 {
762   SilcIDCacheEntry id_cache = NULL;
763   SilcChannelEntry channel;
764
765   if (!id)
766     return NULL;
767
768   SILC_LOG_DEBUG(("Channel ID (%s)",
769                   silc_id_render(id, SILC_ID_CHANNEL)));
770
771   if (!silc_idcache_find_by_id_one(id_list->channels, (void *)id, &id_cache))
772     return NULL;
773
774   channel = (SilcChannelEntry)id_cache->context;
775
776   if (ret_entry)
777     *ret_entry = id_cache;
778
779   SILC_LOG_DEBUG(("Found"));
780
781   /* Touch channel */
782   channel->updated = time(NULL);
783
784   return channel;
785 }
786
787 /* Replaces old Channel ID with new one. This is done when router forces
788    normal server to change Channel ID. */
789
790 SilcChannelEntry
791 silc_idlist_replace_channel_id(SilcIDList id_list, SilcChannelID *old_id,
792                                SilcChannelID *new_id)
793 {
794   SilcIDCacheEntry id_cache = NULL;
795   SilcChannelEntry channel;
796   char *name;
797
798   if (!old_id || !new_id)
799     return NULL;
800
801   SILC_LOG_DEBUG(("Replacing Channel ID"));
802
803   if (!silc_idcache_find_by_id_one(id_list->channels, (void *)old_id,
804                                    &id_cache))
805     return NULL;
806
807   channel = (SilcChannelEntry)id_cache->context;
808   name = strdup(id_cache->name);
809
810   /* Remove the old entry and add a new one */
811
812   silc_idcache_del_by_id(id_list->channels, (void *)channel->id, NULL);
813   *channel->id = *new_id;
814   silc_idcache_add(id_list->channels, name, channel->id, channel);
815
816   SILC_LOG_DEBUG(("Replaced"));
817
818   /* Touch channel */
819   channel->updated = time(NULL);
820
821   return channel;
822 }
823
824 /* Returns channels from the ID list. If the `channel_id' is NULL then
825    all channels are returned. */
826
827 SilcChannelEntry *
828 silc_idlist_get_channels(SilcIDList id_list, SilcChannelID *channel_id,
829                          SilcUInt32 *channels_count)
830 {
831   SilcList list;
832   SilcIDCacheEntry id_cache = NULL;
833   SilcChannelEntry *channels = NULL;
834   int i = 0;
835
836   SILC_LOG_DEBUG(("Start"));
837
838   if (!channel_id) {
839     if (!silc_idcache_get_all(id_list->channels, &list))
840       return NULL;
841
842     channels = silc_calloc(silc_list_count(list), sizeof(*channels));
843
844     i = 0;
845     silc_list_start(list);
846     while ((id_cache = silc_list_get(list)))
847       channels[i++] = (SilcChannelEntry)id_cache->context;
848   } else {
849     if (!silc_idcache_find_by_id_one(id_list->channels, channel_id, &id_cache))
850       return NULL;
851
852     i = 1;
853     channels = silc_calloc(1, sizeof(*channels));
854     channels[0] = (SilcChannelEntry)id_cache->context;
855   }
856
857   if (channels_count)
858     *channels_count = i;
859
860   return channels;
861 }