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