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 - 2005 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   char *server_namec = NULL;
123
124   SILC_LOG_DEBUG(("Adding new server entry"));
125
126   /* Normalize name.  This is cached, original is in server context.  */
127   if (server_name) {
128     server_namec = silc_identifier_check(server_name, strlen(server_name),
129                                          SILC_STRING_UTF8, 256, NULL);
130     if (!server_namec)
131       return NULL;
132   }
133
134   server = silc_calloc(1, sizeof(*server));
135   server->server_name = server_name;
136   server->server_type = server_type;
137   server->id = id;
138   server->router = router;
139   server->connection = connection;
140
141   if (!silc_idcache_add(id_list->servers, server_namec,
142                         (void *)server->id, (void *)server, 0, NULL)) {
143     silc_free(server);
144     silc_free(server_namec);
145     return NULL;
146   }
147
148   return server;
149 }
150
151 /* Finds server by Server ID */
152
153 SilcServerEntry
154 silc_idlist_find_server_by_id(SilcIDList id_list, SilcServerID *id,
155                               bool registered, SilcIDCacheEntry *ret_entry)
156 {
157   SilcIDCacheEntry id_cache = NULL;
158   SilcServerEntry server;
159
160   if (!id)
161     return NULL;
162
163   SILC_LOG_DEBUG(("Server ID (%s)",
164                   silc_id_render(id, SILC_ID_SERVER)));
165
166   if (!silc_idcache_find_by_id_one(id_list->servers, (void *)id,
167                                    &id_cache))
168     return NULL;
169
170   server = (SilcServerEntry)id_cache->context;
171
172   if (server && registered &&
173       !(server->data.status & SILC_IDLIST_STATUS_REGISTERED))
174     return NULL;
175
176   if (ret_entry)
177     *ret_entry = id_cache;
178
179   SILC_LOG_DEBUG(("Found"));
180
181   return server;
182 }
183
184 /* Find server by name.  The 'name' must be normalized already. */
185
186 SilcServerEntry
187 silc_idlist_find_server_by_name(SilcIDList id_list, char *name,
188                                 bool registered, SilcIDCacheEntry *ret_entry)
189 {
190   SilcIDCacheEntry id_cache = NULL;
191   SilcServerEntry server;
192
193   SILC_LOG_DEBUG(("Server by name `%s'", name));
194
195   if (!silc_idcache_find_by_name_one(id_list->servers, name, &id_cache))
196     return NULL;
197
198   server = (SilcServerEntry)id_cache->context;
199
200   if (server && registered &&
201       !(server->data.status & SILC_IDLIST_STATUS_REGISTERED))
202     return NULL;
203
204   if (ret_entry)
205     *ret_entry = id_cache;
206
207   SILC_LOG_DEBUG(("Found"));
208
209   return server;
210 }
211
212 /* Find server by connection parameters, hostname and port */
213
214 SilcServerEntry
215 silc_idlist_find_server_by_conn(SilcIDList id_list, char *hostname,
216                                 int port, bool registered,
217                                 SilcIDCacheEntry *ret_entry)
218 {
219   SilcIDCacheList list = NULL;
220   SilcIDCacheEntry id_cache = NULL;
221   SilcServerEntry server = NULL;
222   SilcSocketConnection sock;
223
224   SILC_LOG_DEBUG(("Server by hostname %s and port %d", hostname, port));
225
226   if (!silc_idcache_get_all(id_list->servers, &list))
227     return NULL;
228
229   if (!silc_idcache_list_first(list, &id_cache)) {
230     silc_idcache_list_free(list);
231     return NULL;
232   }
233
234   while (id_cache) {
235     server = (SilcServerEntry)id_cache->context;
236     sock = (SilcSocketConnection)server->connection;
237
238     if (sock && ((sock->hostname && !strcasecmp(sock->hostname, hostname)) ||
239                  (sock->ip && !strcasecmp(sock->ip, hostname)))
240         && server->id->port == SILC_SWAB_16(port))
241       break;
242
243     id_cache = NULL;
244     server = NULL;
245
246     if (!silc_idcache_list_next(list, &id_cache))
247       break;
248   }
249
250   silc_idcache_list_free(list);
251
252   if (server && registered &&
253       !(server->data.status & SILC_IDLIST_STATUS_REGISTERED))
254     return NULL;
255
256   if (ret_entry)
257     *ret_entry = id_cache;
258
259   SILC_LOG_DEBUG(("Found"));
260
261   return server;
262 }
263
264 /* Replaces old Server ID with new one */
265
266 SilcServerEntry
267 silc_idlist_replace_server_id(SilcIDList id_list, SilcServerID *old_id,
268                               SilcServerID *new_id)
269 {
270   SilcIDCacheEntry id_cache = NULL;
271   SilcServerEntry server;
272   char *name;
273
274   if (!old_id || !new_id)
275     return NULL;
276
277   SILC_LOG_DEBUG(("Replacing Server ID"));
278
279   if (!silc_idcache_find_by_id_one(id_list->servers, (void *)old_id,
280                                    &id_cache))
281     return NULL;
282
283   server = (SilcServerEntry)id_cache->context;
284   name = strdup(id_cache->name);
285
286   /* Remove the old entry and add a new one */
287
288   silc_idcache_del_by_id(id_list->servers, (void *)server->id);
289
290   silc_free(server->id);
291   server->id = new_id;
292
293   silc_idcache_add(id_list->servers, name, server->id, server, 0, NULL);
294
295   SILC_LOG_DEBUG(("Found"));
296
297   return server;
298 }
299
300 /* Removes and free's server entry from ID list */
301
302 int silc_idlist_del_server(SilcIDList id_list, SilcServerEntry entry)
303 {
304   if (entry) {
305     /* Remove from cache */
306     if (!silc_idcache_del_by_context(id_list->servers, entry)) {
307       SILC_LOG_DEBUG(("Unknown server, did not delete"));
308       return FALSE;
309     }
310
311     SILC_LOG_DEBUG(("Deleting server %s id %s", entry->server_name ?
312                     entry->server_name : "",
313                     entry->id ?
314                     silc_id_render(entry->id, SILC_ID_SERVER) : ""));
315
316     /* Free data */
317     silc_free(entry->server_name);
318     silc_free(entry->id);
319     silc_free(entry->server_info);
320
321     memset(entry, 'F', sizeof(*entry));
322     silc_free(entry);
323     return TRUE;
324   }
325
326   return FALSE;
327 }
328
329 /******************************************************************************
330
331                           Client entry functions
332
333 ******************************************************************************/
334
335 /* Add new client entry. This adds the client entry to ID cache system
336    and returns the allocated client entry or NULL on error.  This is
337    called when new client connection is accepted to the server. If The
338    `router' is provided then the all server routines assume that the client
339    is not directly connected local client but it has router set and is
340    remote.  If this is the case then `connection' must be NULL.  If, on the
341    other hand, the `connection' is provided then the client is assumed
342    to be directly connected local client and `router' must be NULL. */
343
344 SilcClientEntry
345 silc_idlist_add_client(SilcIDList id_list, char *nickname, char *username,
346                        char *userinfo, SilcClientID *id,
347                        SilcServerEntry router, void *connection,
348                        int expire)
349 {
350   SilcClientEntry client;
351   char *nicknamec = NULL;
352
353   SILC_LOG_DEBUG(("Adding new client entry"));
354
355   /* Normalize name.  This is cached, original is in client context.  */
356   if (nickname) {
357     nicknamec = silc_identifier_check(nickname, strlen(nickname),
358                                       SILC_STRING_UTF8, 128, NULL);
359     if (!nicknamec)
360       return NULL;
361   }
362
363   /* Check username. */
364   if (username) {
365     char *u = NULL, *h = NULL;
366     silc_parse_userfqdn(username, &u, &h);
367     if (!u)
368       return NULL;
369     if (!silc_identifier_verify(u, strlen(u), SILC_STRING_UTF8, 128)) {
370       silc_free(u);
371       silc_free(h);
372       return NULL;
373     }
374     if (h && !silc_identifier_verify(h, strlen(h), SILC_STRING_UTF8, 256)) {
375       silc_free(u);
376       silc_free(h);
377       return NULL;
378     }
379   }
380
381   client = silc_calloc(1, sizeof(*client));
382   client->nickname = nickname;
383   client->username = username ? strdup(username) : NULL;
384   client->userinfo = userinfo;
385   client->id = id;
386   client->router = router;
387   client->connection = connection;
388   client->channels = silc_hash_table_alloc(3, silc_hash_ptr, NULL,
389                                            NULL, NULL, NULL, NULL, TRUE);
390
391   if (!silc_idcache_add(id_list->clients, nicknamec, (void *)client->id,
392                         (void *)client, expire, NULL)) {
393     silc_hash_table_free(client->channels);
394     silc_free(client);
395     silc_free(nicknamec);
396     return NULL;
397   }
398
399   return client;
400 }
401
402 /* Free client entry. This free's everything and removes the entry
403    from ID cache. Call silc_idlist_del_data before calling this one. */
404
405 int silc_idlist_del_client(SilcIDList id_list, SilcClientEntry entry)
406 {
407   SILC_LOG_DEBUG(("Start"));
408
409   if (entry) {
410     if (!silc_idcache_del_by_context(id_list->clients, entry)) {
411       SILC_LOG_DEBUG(("Unknown client, did not delete"));
412       return FALSE;
413     }
414
415     assert(!silc_hash_table_count(entry->channels));
416
417     silc_free(entry->nickname);
418     silc_free(entry->servername);
419     silc_free(entry->username);
420     silc_free(entry->userinfo);
421     silc_free(entry->id);
422     silc_free(entry->attrs);
423     silc_hash_table_free(entry->channels);
424
425     memset(entry, 'F', sizeof(*entry));
426     silc_free(entry);
427
428     return TRUE;
429   }
430
431   return FALSE;
432 }
433
434 /* ID Cache destructor */
435
436 void silc_idlist_client_destructor(SilcIDCache cache,
437                                    SilcIDCacheEntry entry,
438                                    void *context)
439 {
440   SilcServer server = context;
441   SilcClientEntry client;
442
443   client = (SilcClientEntry)entry->context;
444   if (client) {
445     /* Remove this client from the public key hash list */
446     if (client->data.public_key)
447       silc_hash_table_del_by_context(server->pk_hash,
448                                      client->data.public_key, client);
449
450     assert(!silc_hash_table_count(client->channels));
451     silc_free(client->nickname);
452     silc_free(client->servername);
453     silc_free(client->username);
454     silc_free(client->userinfo);
455     silc_free(client->id);
456     silc_free(client->attrs);
457     if (client->data.public_key)
458       silc_pkcs_public_key_free(client->data.public_key);
459     silc_hash_table_free(client->channels);
460
461     memset(client, 'A', sizeof(*client));
462     silc_free(client);
463   }
464 }
465
466 /* Returns all clients matching requested nickname. Number of clients is
467    returned to `clients_count'. Caller must free the returned table.
468    The 'nickname' must be normalized already. */
469
470 int silc_idlist_get_clients_by_nickname(SilcIDList id_list, char *nickname,
471                                         char *server,
472                                         SilcClientEntry **clients,
473                                         SilcUInt32 *clients_count)
474 {
475   SilcIDCacheList list = NULL;
476   SilcIDCacheEntry id_cache = NULL;
477
478   SILC_LOG_DEBUG(("Start"));
479
480   if (!silc_idcache_find_by_name(id_list->clients, nickname, &list))
481     return FALSE;
482
483   *clients = silc_realloc(*clients,
484                           (silc_idcache_list_count(list) + *clients_count) *
485                           sizeof(**clients));
486
487   silc_idcache_list_first(list, &id_cache);
488   (*clients)[(*clients_count)++] = (SilcClientEntry)id_cache->context;
489
490   while (silc_idcache_list_next(list, &id_cache))
491     (*clients)[(*clients_count)++] = (SilcClientEntry)id_cache->context;
492   silc_idcache_list_free(list);
493
494   SILC_LOG_DEBUG(("Found total %d clients", *clients_count));
495
496   return TRUE;
497 }
498
499 /* Returns all clients matching requested nickname hash. Number of clients
500    is returned to `clients_count'. Caller must free the returned table.
501    The 'nickname' must be normalized already. */
502
503 int silc_idlist_get_clients_by_hash(SilcIDList id_list, char *nickname,
504                                     SilcHash md5hash,
505                                     SilcClientEntry **clients,
506                                     SilcUInt32 *clients_count)
507 {
508   SilcIDCacheList list = NULL;
509   SilcIDCacheEntry id_cache = NULL;
510   unsigned char hash[32];
511   SilcClientID client_id;
512
513   SILC_LOG_DEBUG(("Start"));
514
515   silc_hash_make(md5hash, nickname, strlen(nickname), hash);
516
517   /* As the Client ID is hashed in the ID cache by hashing only the hash
518      from the Client ID, we can do a lookup with only the hash not the
519      other parts of the ID and get all the clients with that hash, ie.
520      with that nickname, as the hash is from the nickname. */
521   memset(&client_id, 0, sizeof(client_id));
522   memcpy(&client_id.hash, hash, sizeof(client_id.hash));
523   if (!silc_idcache_find_by_id(id_list->clients, &client_id, &list))
524     return FALSE;
525
526   *clients = silc_realloc(*clients,
527                           (silc_idcache_list_count(list) + *clients_count) *
528                           sizeof(**clients));
529
530   silc_idcache_list_first(list, &id_cache);
531   (*clients)[(*clients_count)++] = (SilcClientEntry)id_cache->context;
532
533   while (silc_idcache_list_next(list, &id_cache))
534     (*clients)[(*clients_count)++] = (SilcClientEntry)id_cache->context;
535
536   silc_idcache_list_free(list);
537
538   SILC_LOG_DEBUG(("Found total %d clients", *clients_count));
539
540   return TRUE;
541 }
542
543 /* Finds client by Client ID */
544
545 SilcClientEntry
546 silc_idlist_find_client_by_id(SilcIDList id_list, SilcClientID *id,
547                               bool registered, SilcIDCacheEntry *ret_entry)
548 {
549   SilcIDCacheEntry id_cache = NULL;
550   SilcClientEntry client;
551
552   if (!id)
553     return NULL;
554
555   SILC_LOG_DEBUG(("Client ID (%s)",
556                   silc_id_render(id, SILC_ID_CLIENT)));
557
558   /* Do extended search since the normal ID comparison function for
559      Client ID's compares only the hash from the Client ID and not the
560      entire ID. The silc_hash_client_id_compare compares the entire
561      Client ID as we want to find one specific Client ID. */
562   if (!silc_idcache_find_by_id_one_ext(id_list->clients, (void *)id,
563                                        NULL, NULL,
564                                        silc_hash_client_id_compare, NULL,
565                                        &id_cache))
566     return NULL;
567
568   client = (SilcClientEntry)id_cache->context;
569
570   if (client && registered &&
571       !(client->data.status & SILC_IDLIST_STATUS_REGISTERED))
572     return NULL;
573
574   if (ret_entry)
575     *ret_entry = id_cache;
576
577   SILC_LOG_DEBUG(("Found"));
578
579   return client;
580 }
581
582 /* Replaces old Client ID with new one */
583
584 SilcClientEntry
585 silc_idlist_replace_client_id(SilcServer server,
586                               SilcIDList id_list, SilcClientID *old_id,
587                               SilcClientID *new_id, const char *nickname)
588 {
589   SilcIDCacheEntry id_cache = NULL;
590   SilcClientEntry client;
591   char *nicknamec = NULL;
592
593   if (!old_id || !new_id)
594     return NULL;
595
596   SILC_LOG_DEBUG(("Replacing Client ID"));
597
598   /* Normalize name. This is cached, original is in client context.  */
599   if (nickname) {
600     nicknamec = silc_identifier_check(nickname, strlen(nickname),
601                                       SILC_STRING_UTF8, 128, NULL);
602     if (!nicknamec)
603       return NULL;
604   }
605
606   /* Do extended search since the normal ID comparison function for
607      Client ID's compares only the hash from the Client ID and not the
608      entire ID. The silc_hash_client_id_compare compares the entire
609      Client ID as we want to find one specific Client ID. */
610   if (!silc_idcache_find_by_id_one_ext(id_list->clients, (void *)old_id,
611                                        NULL, NULL,
612                                        silc_hash_client_id_compare, NULL,
613                                        &id_cache))
614     return NULL;
615
616   client = (SilcClientEntry)id_cache->context;
617
618   /* Remove the old entry and add a new one */
619
620   if (!silc_idcache_del_by_context(id_list->clients, client))
621     return NULL;
622
623   /* Check if anyone is watching old nickname */
624   if (server->server_type == SILC_ROUTER)
625     silc_server_check_watcher_list(server, client, nickname,
626                                    SILC_NOTIFY_TYPE_NICK_CHANGE);
627
628   silc_free(client->id);
629   silc_free(client->nickname);
630   client->id = new_id;
631   client->nickname = nickname ? strdup(nickname) : NULL;
632
633   /* Check if anyone is watching new nickname */
634   if (server->server_type == SILC_ROUTER)
635     silc_server_check_watcher_list(server, client, nickname,
636                                    SILC_NOTIFY_TYPE_NICK_CHANGE);
637
638   if (!silc_idcache_add(id_list->clients, nicknamec, client->id,
639                         client, 0, NULL))
640     return NULL;
641
642   SILC_LOG_DEBUG(("Replaced"));
643
644   return client;
645 }
646
647
648 /******************************************************************************
649
650                           Channel entry functions
651
652 ******************************************************************************/
653
654 /* Add new channel entry. This add the new channel entry to the ID cache
655    system and returns the allocated entry or NULL on error. */
656
657 SilcChannelEntry
658 silc_idlist_add_channel(SilcIDList id_list, char *channel_name, int mode,
659                         SilcChannelID *id, SilcServerEntry router,
660                         SilcCipher channel_key, SilcHmac hmac,
661                         int expire)
662 {
663   SilcChannelEntry channel;
664   char *channel_namec = NULL;
665
666   SILC_LOG_DEBUG(("Adding new channel %s", channel_name));
667
668   /* Normalize name.  This is cached, original is in client context.  */
669   if (channel_name) {
670     channel_namec = silc_channel_name_check(channel_name, strlen(channel_name),
671                                             SILC_STRING_UTF8, 256, NULL);
672     if (!channel_namec)
673       return NULL;
674   }
675
676   channel = silc_calloc(1, sizeof(*channel));
677   channel->channel_name = channel_name;
678   channel->mode = mode;
679   channel->id = id;
680   channel->router = router;
681   channel->channel_key = channel_key;
682   channel->hmac = hmac;
683   channel->created = channel->updated = time(0);
684   if (!channel->hmac)
685     if (!silc_hmac_alloc(SILC_DEFAULT_HMAC, NULL, &channel->hmac)) {
686       silc_free(channel);
687       return NULL;
688     }
689
690   channel->user_list = silc_hash_table_alloc(3, silc_hash_ptr, NULL, NULL,
691                                              NULL, NULL, NULL, TRUE);
692
693   if (!silc_idcache_add(id_list->channels, channel_namec,
694                         (void *)channel->id, (void *)channel, expire, NULL)) {
695     silc_hmac_free(channel->hmac);
696     silc_hash_table_free(channel->user_list);
697     silc_free(channel);
698     silc_free(channel_namec);
699     return NULL;
700   }
701
702   return channel;
703 }
704
705 /* Foreach callbcak to free all users from the channel when deleting a
706    channel entry. */
707
708 static void silc_idlist_del_channel_foreach(void *key, void *context,
709                                             void *user_context)
710 {
711   SilcChannelClientEntry chl = (SilcChannelClientEntry)context;
712
713   SILC_LOG_DEBUG(("Removing client %s from channel %s",
714                   chl->client->nickname ? chl->client->nickname :
715                   (unsigned char *)"", chl->channel->channel_name));
716
717   /* Remove the context from the client's channel hash table as that
718      table and channel's user_list hash table share this same context. */
719   silc_hash_table_del(chl->client->channels, chl->channel);
720   silc_free(chl);
721 }
722
723 /* Free channel entry.  This free's everything. */
724
725 int silc_idlist_del_channel(SilcIDList id_list, SilcChannelEntry entry)
726 {
727   if (entry) {
728     /* Remove from cache */
729     if (!silc_idcache_del_by_context(id_list->channels, entry)) {
730       SILC_LOG_DEBUG(("Unknown channel, did not delete"));
731       return FALSE;
732     }
733
734     SILC_LOG_DEBUG(("Deleting channel %s", entry->channel_name));
735
736     /* Free all client entrys from the users list. The silc_hash_table_free
737        will free all the entries so they are not freed at the foreach
738        callback. */
739     silc_hash_table_foreach(entry->user_list, silc_idlist_del_channel_foreach,
740                             NULL);
741     silc_hash_table_free(entry->user_list);
742
743     /* Free data */
744     silc_free(entry->channel_name);
745     silc_free(entry->id);
746     silc_free(entry->topic);
747
748     if (entry->invite_list)
749       silc_hash_table_free(entry->invite_list);
750     if (entry->ban_list)
751       silc_hash_table_free(entry->ban_list);
752
753     if (entry->channel_key)
754       silc_cipher_free(entry->channel_key);
755     if (entry->key) {
756       memset(entry->key, 0, entry->key_len / 8);
757       silc_free(entry->key);
758     }
759     silc_free(entry->cipher);
760     if (entry->hmac)
761       silc_hmac_free(entry->hmac);
762     silc_free(entry->hmac_name);
763     silc_free(entry->rekey);
764     if (entry->founder_key)
765       silc_pkcs_public_key_free(entry->founder_key);
766     if (entry->channel_pubkeys)
767       silc_hash_table_free(entry->channel_pubkeys);
768
769     memset(entry, 'F', sizeof(*entry));
770     silc_free(entry);
771     return TRUE;
772   }
773
774   return FALSE;
775 }
776
777 /* Finds channel by channel name. Channel names are unique and they
778    are not case-sensitive.  The 'name' must be normalized already. */
779
780 SilcChannelEntry
781 silc_idlist_find_channel_by_name(SilcIDList id_list, char *name,
782                                  SilcIDCacheEntry *ret_entry)
783 {
784   SilcIDCacheEntry id_cache = NULL;
785
786   SILC_LOG_DEBUG(("Channel by name %s", name));
787
788   if (!silc_idcache_find_by_name_one(id_list->channels, name, &id_cache))
789     return NULL;
790
791   if (ret_entry)
792     *ret_entry = id_cache;
793
794   SILC_LOG_DEBUG(("Found"));
795
796   /* Touch channel */
797   ((SilcChannelEntry)id_cache->context)->updated = time(NULL);
798
799   return id_cache->context;
800 }
801
802 /* Finds channel by Channel ID. */
803
804 SilcChannelEntry
805 silc_idlist_find_channel_by_id(SilcIDList id_list, SilcChannelID *id,
806                                SilcIDCacheEntry *ret_entry)
807 {
808   SilcIDCacheEntry id_cache = NULL;
809   SilcChannelEntry channel;
810
811   if (!id)
812     return NULL;
813
814   SILC_LOG_DEBUG(("Channel ID (%s)",
815                   silc_id_render(id, SILC_ID_CHANNEL)));
816
817   if (!silc_idcache_find_by_id_one(id_list->channels, (void *)id, &id_cache))
818     return NULL;
819
820   channel = (SilcChannelEntry)id_cache->context;
821
822   if (ret_entry)
823     *ret_entry = id_cache;
824
825   SILC_LOG_DEBUG(("Found"));
826
827   /* Touch channel */
828   channel->updated = time(NULL);
829
830   return channel;
831 }
832
833 /* Replaces old Channel ID with new one. This is done when router forces
834    normal server to change Channel ID. */
835
836 SilcChannelEntry
837 silc_idlist_replace_channel_id(SilcIDList id_list, SilcChannelID *old_id,
838                                SilcChannelID *new_id)
839 {
840   SilcIDCacheEntry id_cache = NULL;
841   SilcChannelEntry channel;
842   char *name;
843
844   if (!old_id || !new_id)
845     return NULL;
846
847   SILC_LOG_DEBUG(("Replacing Channel ID"));
848
849   if (!silc_idcache_find_by_id_one(id_list->channels, (void *)old_id,
850                                    &id_cache))
851     return NULL;
852
853   channel = (SilcChannelEntry)id_cache->context;
854   name = strdup(id_cache->name);
855
856   /* Remove the old entry and add a new one */
857
858   silc_idcache_del_by_id(id_list->channels, (void *)channel->id);
859
860   silc_free(channel->id);
861   channel->id = new_id;
862
863   silc_idcache_add(id_list->channels, name, channel->id, channel, 0, NULL);
864
865   SILC_LOG_DEBUG(("Replaced"));
866
867   /* Touch channel */
868   channel->updated = time(NULL);
869
870   return channel;
871 }
872
873 /* Returns channels from the ID list. If the `channel_id' is NULL then
874    all channels are returned. */
875
876 SilcChannelEntry *
877 silc_idlist_get_channels(SilcIDList id_list, SilcChannelID *channel_id,
878                          SilcUInt32 *channels_count)
879 {
880   SilcIDCacheList list = NULL;
881   SilcIDCacheEntry id_cache = NULL;
882   SilcChannelEntry *channels = NULL;
883   int i = 0;
884
885   SILC_LOG_DEBUG(("Start"));
886
887   if (!channel_id) {
888     if (!silc_idcache_get_all(id_list->channels, &list))
889       return NULL;
890
891     channels = silc_calloc(silc_idcache_list_count(list), sizeof(*channels));
892
893     i = 0;
894     silc_idcache_list_first(list, &id_cache);
895     channels[i++] = (SilcChannelEntry)id_cache->context;
896
897     while (silc_idcache_list_next(list, &id_cache))
898       channels[i++] = (SilcChannelEntry)id_cache->context;
899
900     silc_idcache_list_free(list);
901   } else {
902     if (!silc_idcache_find_by_id_one(id_list->channels, channel_id, &id_cache))
903       return NULL;
904
905     i = 1;
906     channels = silc_calloc(1, sizeof(*channels));
907     channels[0] = (SilcChannelEntry)id_cache->context;
908   }
909
910   if (channels_count)
911     *channels_count = i;
912
913   return channels;
914 }