a29ba9fb1f9a1817e182917437ef706ab71f511b
[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->public_key)
59     silc_pkcs_public_key_free(idata->public_key);
60
61   idata->hash = NULL;
62   idata->public_key = NULL;
63 }
64
65 /******************************************************************************
66
67                           Server entry functions
68
69 ******************************************************************************/
70
71 /* Add new server entry. This adds the new server entry to ID cache and
72    returns the allocated entry object or NULL on error. This is called
73    when new server connects to us. We also add ourselves to cache with
74    this function. */
75
76 SilcServerEntry
77 silc_idlist_add_server(SilcIDList id_list,
78                        char *server_name, int server_type,
79                        SilcServerID *id, SilcServerEntry router,
80                        void *connection)
81 {
82   SilcServerEntry server;
83   char *server_namec = NULL;
84
85   SILC_LOG_DEBUG(("Adding new server entry"));
86
87   /* Normalize name.  This is cached, original is in server context.  */
88   if (server_name) {
89     server_namec = silc_identifier_check(server_name, strlen(server_name),
90                                          SILC_STRING_UTF8, 256, NULL);
91     if (!server_namec)
92       return NULL;
93   }
94
95   server = silc_calloc(1, sizeof(*server));
96   server->server_name = server_name;
97   server->server_type = server_type;
98   server->id = id;
99   server->router = router;
100   server->connection = connection;
101
102   if (!silc_idcache_add(id_list->servers, server_namec,
103                         (void *)server->id, (void *)server)) {
104     silc_free(server);
105     silc_free(server_namec);
106     return NULL;
107   }
108
109   return server;
110 }
111
112 /* Finds server by Server ID */
113
114 SilcServerEntry
115 silc_idlist_find_server_by_id(SilcIDList id_list, SilcServerID *id,
116                               SilcBool registered, SilcIDCacheEntry *ret_entry)
117 {
118   SilcIDCacheEntry id_cache = NULL;
119   SilcServerEntry server;
120
121   if (!id)
122     return NULL;
123
124   SILC_LOG_DEBUG(("Server ID (%s)",
125                   silc_id_render(id, SILC_ID_SERVER)));
126
127   if (!silc_idcache_find_by_id_one(id_list->servers, (void *)id, &id_cache))
128     return NULL;
129
130   server = (SilcServerEntry)id_cache->context;
131
132   if (server && registered &&
133       !(server->data.status & SILC_IDLIST_STATUS_REGISTERED))
134     return NULL;
135
136   if (ret_entry)
137     *ret_entry = id_cache;
138
139   SILC_LOG_DEBUG(("Found"));
140
141   return server;
142 }
143
144 /* Find server by name.  The 'name' must be normalized already. */
145
146 SilcServerEntry
147 silc_idlist_find_server_by_name(SilcIDList id_list, char *name,
148                                 SilcBool registered,
149                                 SilcIDCacheEntry *ret_entry)
150 {
151   SilcIDCacheEntry id_cache = NULL;
152   SilcServerEntry server;
153
154   SILC_LOG_DEBUG(("Server by name `%s'", name));
155
156   if (!silc_idcache_find_by_name_one(id_list->servers, name, &id_cache))
157     return NULL;
158
159   server = (SilcServerEntry)id_cache->context;
160
161   if (server && registered &&
162       !(server->data.status & SILC_IDLIST_STATUS_REGISTERED))
163     return NULL;
164
165   if (ret_entry)
166     *ret_entry = id_cache;
167
168   SILC_LOG_DEBUG(("Found"));
169
170   return server;
171 }
172
173 /* Find server by connection parameters, hostname and port */
174
175 SilcServerEntry
176 silc_idlist_find_server_by_conn(SilcIDList id_list, char *hostname,
177                                 int port, SilcBool registered,
178                                 SilcIDCacheEntry *ret_entry)
179 {
180   SilcList list;
181   SilcIDCacheEntry id_cache = NULL;
182   SilcServerEntry server = NULL;
183   SilcPacketStream sock;
184   const char *host = NULL, *ip = NULL;
185
186   SILC_LOG_DEBUG(("Server by hostname %s and port %d", hostname, port));
187
188   if (!silc_idcache_get_all(id_list->servers, &list))
189     return NULL;
190
191   silc_list_start(list);
192   while ((id_cache = silc_list_get(list))) {
193     server = id_cache->context;
194     sock = server->connection;
195
196     if (sock && silc_socket_stream_get_info(
197                             silc_packet_stream_get_stream(sock),
198                             NULL, &host, &ip, NULL)) {
199       if (((host && !strcasecmp(host, hostname)) ||
200            (ip && !strcasecmp(ip, hostname))) &&
201           server->id->port == SILC_SWAB_16(port))
202         break;
203     }
204
205     id_cache = NULL;
206     server = NULL;
207   }
208
209   if (server && registered &&
210       !(server->data.status & SILC_IDLIST_STATUS_REGISTERED))
211     return NULL;
212
213   if (ret_entry)
214     *ret_entry = id_cache;
215
216   SILC_LOG_DEBUG(("Found"));
217
218   return server;
219 }
220
221 /* Replaces old Server ID with new one */
222
223 SilcServerEntry
224 silc_idlist_replace_server_id(SilcIDList id_list, SilcServerID *old_id,
225                               SilcServerID *new_id)
226 {
227   SilcIDCacheEntry id_cache = NULL;
228   SilcServerEntry server;
229   char *name;
230
231   if (!old_id || !new_id)
232     return NULL;
233
234   SILC_LOG_DEBUG(("Replacing Server ID"));
235
236   if (!silc_idcache_find_by_id_one(id_list->servers, (void *)old_id, &id_cache))
237     return NULL;
238
239   server = (SilcServerEntry)id_cache->context;
240   name = strdup(id_cache->name);
241
242   /* Remove the old entry and add a new one */
243
244   silc_idcache_del_by_id(id_list->servers, (void *)server->id, NULL);
245   *server->id = *new_id;
246   silc_idcache_add(id_list->servers, name, server->id, server);
247
248   SILC_LOG_DEBUG(("Found"));
249
250   return server;
251 }
252
253 /* Removes and free's server entry from ID list */
254
255 int silc_idlist_del_server(SilcIDList id_list, SilcServerEntry entry)
256 {
257   if (entry) {
258     /* Remove from cache */
259     if (!silc_idcache_del_by_context(id_list->servers, entry, NULL)) {
260       SILC_LOG_DEBUG(("Unknown server, did not delete"));
261       return FALSE;
262     }
263
264     SILC_LOG_DEBUG(("Deleting server %s id %s", entry->server_name ?
265                     entry->server_name : "",
266                     entry->id ?
267                     silc_id_render(entry->id, SILC_ID_SERVER) : ""));
268
269     /* Free data */
270     silc_free(entry->server_name);
271     silc_free(entry->id);
272     silc_free(entry->server_info);
273
274     memset(entry, 'F', sizeof(*entry));
275     silc_free(entry);
276     return TRUE;
277   }
278
279   return FALSE;
280 }
281
282 /* ID Cache destructor */
283
284 void silc_idlist_server_destructor(SilcIDCache cache,
285                                    SilcIDCacheEntry entry,
286                                    void *dest_context,
287                                    void *app_context)
288 {
289   silc_free(entry->name);
290 }
291
292 /******************************************************************************
293
294                           Client entry functions
295
296 ******************************************************************************/
297
298 /* Add new client entry. This adds the client entry to ID cache system
299    and returns the allocated client entry or NULL on error.  This is
300    called when new client connection is accepted to the server. If The
301    `router' is provided then the all server routines assume that the client
302    is not directly connected local client but it has router set and is
303    remote.  If this is the case then `connection' must be NULL.  If, on the
304    other hand, the `connection' is provided then the client is assumed
305    to be directly connected local client and `router' must be NULL. */
306
307 SilcClientEntry
308 silc_idlist_add_client(SilcIDList id_list, char *nickname, char *username,
309                        char *userinfo, SilcClientID *id,
310                        SilcServerEntry router, void *connection)
311 {
312   SilcClientEntry client;
313   char *nicknamec = NULL;
314
315   SILC_LOG_DEBUG(("Adding new client entry"));
316
317   /* Normalize name.  This is cached, original is in client context.  */
318   if (nickname) {
319     nicknamec = silc_identifier_check(nickname, strlen(nickname),
320                                       SILC_STRING_UTF8, 128, NULL);
321     if (!nicknamec)
322       return NULL;
323   }
324
325   /* Check username. */
326   if (username) {
327     char u[128 + 1], h[256 + 1];
328     int ret;
329
330     ret = silc_parse_userfqdn(username, u, sizeof(u), h, sizeof(h));
331     if (!u)
332       return NULL;
333     if (!silc_identifier_verify(u, strlen(u), SILC_STRING_UTF8, 128))
334       return NULL;
335     if (ret > 1 && !silc_identifier_verify(h, strlen(h),
336                                            SILC_STRING_UTF8, 256))
337       return NULL;
338   }
339
340   client = silc_calloc(1, sizeof(*client));
341   client->nickname = nickname;
342   client->username = username ? strdup(username) : NULL;
343   client->userinfo = userinfo;
344   client->id = id;
345   client->router = router;
346   client->connection = connection;
347   client->channels = silc_hash_table_alloc(3, silc_hash_ptr, NULL,
348                                            NULL, NULL, NULL, NULL, TRUE);
349
350   if (!silc_idcache_add(id_list->clients, nicknamec, (void *)client->id,
351                         (void *)client)) {
352     silc_hash_table_free(client->channels);
353     silc_free(client);
354     silc_free(nicknamec);
355     return NULL;
356   }
357
358   return client;
359 }
360
361 /* Free client entry. This free's everything and removes the entry
362    from ID cache. Call silc_idlist_del_data before calling this one. */
363
364 int silc_idlist_del_client(SilcIDList id_list, SilcClientEntry entry)
365 {
366   SILC_LOG_DEBUG(("Delete client %p", entry));
367
368   if (entry) {
369     /* Delete client, destructor will free data */
370     if (!silc_idcache_del_by_context(id_list->clients, entry, NULL)) {
371       SILC_LOG_DEBUG(("Unknown client, did not delete"));
372       return FALSE;
373     }
374     return TRUE;
375   }
376
377   return FALSE;
378 }
379
380 /* ID Cache destructor */
381
382 void silc_idlist_client_destructor(SilcIDCache cache,
383                                    SilcIDCacheEntry entry,
384                                    void *dest_context,
385                                    void *app_context)
386 {
387   SilcServer server = dest_context;
388   SilcClientEntry client;
389
390   client = (SilcClientEntry)entry->context;
391   if (client) {
392     /* Remove client's public key from repository, this will free it too. */
393     if (client->data.public_key)
394       silc_skr_del_public_key(server->repository, client->data.public_key,
395                               client);
396
397     assert(!silc_hash_table_count(client->channels));
398     silc_free(client->nickname);
399     silc_free(client->servername);
400     silc_free(client->username);
401     silc_free(client->userinfo);
402     silc_free(client->id);
403     silc_free(client->attrs);
404     silc_hash_table_free(client->channels);
405
406     memset(client, 'A', sizeof(*client));
407     silc_free(client);
408   }
409 }
410
411 /* Returns all clients matching requested nickname. Number of clients is
412    returned to `clients_count'. Caller must free the returned table.
413    The 'nickname' must be normalized already. */
414
415 int silc_idlist_get_clients_by_nickname(SilcIDList id_list, char *nickname,
416                                         char *server,
417                                         SilcClientEntry **clients,
418                                         SilcUInt32 *clients_count)
419 {
420   SilcList list;
421   SilcIDCacheEntry id_cache = NULL;
422
423   SILC_LOG_DEBUG(("Start"));
424
425   if (!silc_idcache_find_by_name(id_list->clients, nickname, &list))
426     return FALSE;
427
428   *clients = silc_realloc(*clients,
429                           (silc_list_count(list) + *clients_count) *
430                           sizeof(**clients));
431
432   silc_list_start(list);
433   while ((id_cache = silc_list_get(list)))
434     (*clients)[(*clients_count)++] = id_cache->context;
435
436   SILC_LOG_DEBUG(("Found total %d clients", *clients_count));
437
438   return TRUE;
439 }
440
441 /* Returns all clients matching requested nickname hash. Number of clients
442    is returned to `clients_count'. Caller must free the returned table.
443    The 'nickname' must be normalized already. */
444
445 int silc_idlist_get_clients_by_hash(SilcIDList id_list,
446                                     char *nickname, char *server,
447                                     SilcHash md5hash,
448                                     SilcClientEntry **clients,
449                                     SilcUInt32 *clients_count)
450 {
451   SilcList list;
452   SilcIDCacheEntry id_cache = NULL;
453   unsigned char hash[SILC_HASH_MAXLEN];
454   SilcClientID client_id;
455   SilcClientEntry client_entry;
456
457   SILC_LOG_DEBUG(("Start"));
458
459   silc_hash_make(md5hash, nickname, strlen(nickname), hash);
460
461   /* As the Client ID is hashed in the ID cache by hashing only the hash
462      from the Client ID, we can do a lookup with only the hash not the
463      other parts of the ID and get all the clients with that hash, ie.
464      with that nickname, as the hash is from the nickname. */
465   memset(&client_id, 0, sizeof(client_id));
466   memcpy(&client_id.hash, hash, sizeof(client_id.hash));
467   if (!silc_idcache_find_by_id(id_list->clients, &client_id, &list))
468     return FALSE;
469
470   /* If server is specified, narrow the search with it. */
471   if (server) {
472     silc_list_start(list);
473     while ((id_cache = silc_list_get(list))) {
474       client_entry = id_cache->context;
475       if (!client_entry->servername)
476         continue;
477       if (!silc_utf8_strcasecmp(client_entry->servername, server))
478         silc_list_del(list, id_cache);
479     }
480   }
481
482   if (!silc_list_count(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 }