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