Merge commit 'origin/silc.1.1.branch'
[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
59   idata->hash = NULL;
60   idata->public_key = NULL;
61 }
62
63 /******************************************************************************
64
65                           Server entry functions
66
67 ******************************************************************************/
68
69 /* Add new server entry. This adds the new server entry to ID cache and
70    returns the allocated entry object or NULL on error. This is called
71    when new server connects to us. We also add ourselves to cache with
72    this function. */
73
74 SilcServerEntry
75 silc_idlist_add_server(SilcIDList id_list,
76                        char *server_name, int server_type,
77                        SilcServerID *id, SilcServerEntry router,
78                        void *connection)
79 {
80   SilcServerEntry server;
81   char *server_namec = NULL;
82
83   SILC_LOG_DEBUG(("Adding new server entry"));
84
85   /* Normalize name.  This is cached, original is in server context.  */
86   if (server_name) {
87     server_namec = silc_identifier_check(server_name, strlen(server_name),
88                                          SILC_STRING_UTF8, 256, NULL);
89     if (!server_namec)
90       return NULL;
91   }
92
93   server = silc_calloc(1, sizeof(*server));
94   server->server_name = server_name;
95   server->server_type = server_type;
96   server->id = id;
97   server->router = router;
98   server->connection = connection;
99
100   if (!silc_idcache_add(id_list->servers, server_namec,
101                         (void *)server->id, (void *)server)) {
102     silc_free(server);
103     silc_free(server_namec);
104     return NULL;
105   }
106
107   return server;
108 }
109
110 /* Finds server by Server ID */
111
112 SilcServerEntry
113 silc_idlist_find_server_by_id(SilcIDList id_list, SilcServerID *id,
114                               SilcBool registered, SilcIDCacheEntry *ret_entry)
115 {
116   SilcIDCacheEntry id_cache = NULL;
117   SilcServerEntry server;
118
119   if (!id)
120     return NULL;
121
122   SILC_LOG_DEBUG(("Server ID (%s)",
123                   silc_id_render(id, SILC_ID_SERVER)));
124
125   if (!silc_idcache_find_by_id_one(id_list->servers, (void *)id, &id_cache))
126     return NULL;
127
128   server = (SilcServerEntry)id_cache->context;
129
130   if (server && registered &&
131       !(server->data.status & SILC_IDLIST_STATUS_REGISTERED))
132     return NULL;
133
134   if (ret_entry)
135     *ret_entry = id_cache;
136
137   SILC_LOG_DEBUG(("Found"));
138
139   return server;
140 }
141
142 /* Find server by name.  The 'name' must be normalized already. */
143
144 SilcServerEntry
145 silc_idlist_find_server_by_name(SilcIDList id_list, char *name,
146                                 SilcBool registered,
147                                 SilcIDCacheEntry *ret_entry)
148 {
149   SilcIDCacheEntry id_cache = NULL;
150   SilcServerEntry server;
151
152   SILC_LOG_DEBUG(("Server by name `%s'", name));
153
154   if (!silc_idcache_find_by_name_one(id_list->servers, name, &id_cache))
155     return NULL;
156
157   server = (SilcServerEntry)id_cache->context;
158
159   if (server && registered &&
160       !(server->data.status & SILC_IDLIST_STATUS_REGISTERED))
161     return NULL;
162
163   if (ret_entry)
164     *ret_entry = id_cache;
165
166   SILC_LOG_DEBUG(("Found"));
167
168   return server;
169 }
170
171 /* Find server by connection parameters, hostname and port */
172
173 SilcServerEntry
174 silc_idlist_find_server_by_conn(SilcIDList id_list, char *hostname,
175                                 int port, SilcBool registered,
176                                 SilcIDCacheEntry *ret_entry)
177 {
178   SilcList list;
179   SilcIDCacheEntry id_cache = NULL;
180   SilcServerEntry server = NULL;
181   SilcPacketStream sock;
182   const char *host = NULL, *ip = NULL;
183
184   SILC_LOG_DEBUG(("Server by hostname %s and port %d", hostname, port));
185
186   if (!silc_idcache_get_all(id_list->servers, &list))
187     return NULL;
188
189   silc_list_start(list);
190   while ((id_cache = silc_list_get(list))) {
191     server = id_cache->context;
192     sock = server->connection;
193
194     if (sock && silc_socket_stream_get_info(
195                             silc_packet_stream_get_stream(sock),
196                             NULL, &host, &ip, NULL)) {
197       if (((host && !strcasecmp(host, hostname)) ||
198            (ip && !strcasecmp(ip, hostname))) &&
199           server->id->port == SILC_SWAB_16(port))
200         break;
201     }
202
203     id_cache = NULL;
204     server = NULL;
205   }
206
207   if (server && registered &&
208       !(server->data.status & SILC_IDLIST_STATUS_REGISTERED))
209     return NULL;
210
211   if (ret_entry)
212     *ret_entry = id_cache;
213
214   SILC_LOG_DEBUG(("Found"));
215
216   return server;
217 }
218
219 /* Replaces old Server ID with new one */
220
221 SilcServerEntry
222 silc_idlist_replace_server_id(SilcIDList id_list, SilcServerID *old_id,
223                               SilcServerID *new_id)
224 {
225   SilcIDCacheEntry id_cache = NULL;
226   SilcServerEntry server;
227   char *name;
228
229   if (!old_id || !new_id)
230     return NULL;
231
232   SILC_LOG_DEBUG(("Replacing Server ID"));
233
234   if (!silc_idcache_find_by_id_one(id_list->servers, (void *)old_id, &id_cache))
235     return NULL;
236
237   server = (SilcServerEntry)id_cache->context;
238   name = strdup(id_cache->name);
239
240   /* Remove the old entry and add a new one */
241
242   silc_idcache_del_by_id(id_list->servers, (void *)server->id, NULL);
243   *server->id = *new_id;
244   silc_idcache_add(id_list->servers, name, server->id, server);
245
246   SILC_LOG_DEBUG(("Found"));
247
248   return server;
249 }
250
251 /* Removes and free's server entry from ID list */
252
253 int silc_idlist_del_server(SilcIDList id_list, SilcServerEntry entry)
254 {
255   if (entry) {
256     /* Remove from cache */
257     if (!silc_idcache_del_by_context(id_list->servers, entry, NULL)) {
258       SILC_LOG_DEBUG(("Unknown server, did not delete"));
259       return FALSE;
260     }
261
262     SILC_LOG_DEBUG(("Deleting server %s id %s", entry->server_name ?
263                     entry->server_name : "",
264                     entry->id ?
265                     silc_id_render(entry->id, SILC_ID_SERVER) : ""));
266
267     /* Free data */
268     silc_free(entry->server_name);
269     silc_free(entry->id);
270     silc_free(entry->server_info);
271
272     memset(entry, 'F', sizeof(*entry));
273     silc_free(entry);
274     return TRUE;
275   }
276
277   return FALSE;
278 }
279
280 /* ID Cache destructor */
281
282 void silc_idlist_server_destructor(SilcIDCache cache,
283                                    SilcIDCacheEntry entry,
284                                    void *dest_context,
285                                    void *app_context)
286 {
287   silc_free(entry->name);
288 }
289
290 /******************************************************************************
291
292                           Client entry functions
293
294 ******************************************************************************/
295
296 /* Add new client entry. This adds the client entry to ID cache system
297    and returns the allocated client entry or NULL on error.  This is
298    called when new client connection is accepted to the server. If The
299    `router' is provided then the all server routines assume that the client
300    is not directly connected local client but it has router set and is
301    remote.  If this is the case then `connection' must be NULL.  If, on the
302    other hand, the `connection' is provided then the client is assumed
303    to be directly connected local client and `router' must be NULL. */
304
305 SilcClientEntry
306 silc_idlist_add_client(SilcIDList id_list, char *nickname, char *username,
307                        char *userinfo, SilcClientID *id,
308                        SilcServerEntry router, void *connection)
309 {
310   SilcClientEntry client;
311   char *nicknamec = NULL;
312
313   SILC_LOG_DEBUG(("Adding new client entry"));
314
315   /* Normalize name.  This is cached, original is in client context.  */
316   if (nickname) {
317     nicknamec = silc_identifier_check(nickname, strlen(nickname),
318                                       SILC_STRING_UTF8, 128, NULL);
319     if (!nicknamec)
320       return NULL;
321   }
322
323   /* Check username. */
324   if (username) {
325     char u[128 + 1], h[256 + 1];
326     int ret;
327
328     ret = silc_parse_userfqdn(username, u, sizeof(u), h, sizeof(h));
329     if (!ret)
330       return NULL;
331     if (!silc_identifier_verify(u, strlen(u), SILC_STRING_UTF8, 128))
332       return NULL;
333     if (ret > 1 && !silc_identifier_verify(h, strlen(h),
334                                            SILC_STRING_UTF8, 256))
335       return NULL;
336   }
337
338   client = silc_calloc(1, sizeof(*client));
339   if (!client)
340     return NULL;
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   /* Check if anyone is watching old nickname */
562   if (server->server_type == SILC_ROUTER)
563     silc_server_check_watcher_list(server, client, nickname,
564                                    SILC_NOTIFY_TYPE_NICK_CHANGE);
565
566   /* Replace */
567   if (!silc_idcache_update(id_list->clients, id_cache, new_id, nicknamec,
568                            TRUE))
569     return NULL;
570
571   silc_free(client->nickname);
572   client->nickname = nickname ? strdup(nickname) : NULL;
573
574   /* Check if anyone is watching new nickname */
575   if (server->server_type == SILC_ROUTER)
576     silc_server_check_watcher_list(server, client, nickname,
577                                    SILC_NOTIFY_TYPE_NICK_CHANGE);
578
579   SILC_LOG_DEBUG(("Replaced"));
580
581   return client;
582 }
583
584
585 /******************************************************************************
586
587                           Channel entry functions
588
589 ******************************************************************************/
590
591 /* Add new channel entry. This add the new channel entry to the ID cache
592    system and returns the allocated entry or NULL on error. */
593
594 SilcChannelEntry
595 silc_idlist_add_channel(SilcIDList id_list, char *channel_name, int mode,
596                         SilcChannelID *id, SilcServerEntry router,
597                         SilcCipher send_key, SilcCipher receive_key,
598                         SilcHmac hmac)
599 {
600   SilcChannelEntry channel;
601   char *channel_namec = NULL;
602
603   SILC_LOG_DEBUG(("Adding new channel %s", channel_name));
604
605   /* Normalize name.  This is cached, original is in client context.  */
606   if (channel_name) {
607     channel_namec = silc_channel_name_check(channel_name, strlen(channel_name),
608                                             SILC_STRING_UTF8, 256, NULL);
609     if (!channel_namec)
610       return NULL;
611   }
612
613   channel = silc_calloc(1, sizeof(*channel));
614   channel->channel_name = channel_name;
615   channel->mode = mode;
616   channel->id = id;
617   channel->router = router;
618   channel->send_key = send_key;
619   channel->receive_key = receive_key;
620   channel->hmac = hmac;
621   channel->created = channel->updated = time(0);
622   if (!channel->hmac)
623     if (!silc_hmac_alloc(SILC_DEFAULT_HMAC, NULL, &channel->hmac)) {
624       silc_free(channel);
625       return NULL;
626     }
627
628   channel->user_list = silc_hash_table_alloc(3, silc_hash_ptr, NULL, NULL,
629                                              NULL, NULL, NULL, TRUE);
630
631   if (!silc_idcache_add(id_list->channels, channel_namec,
632                         (void *)channel->id, (void *)channel)) {
633     silc_hmac_free(channel->hmac);
634     silc_hash_table_free(channel->user_list);
635     silc_free(channel);
636     silc_free(channel_namec);
637     return NULL;
638   }
639
640   return channel;
641 }
642
643 /* ID Cache destructor */
644
645 void silc_idlist_channel_destructor(SilcIDCache cache,
646                                     SilcIDCacheEntry entry,
647                                     void *dest_context,
648                                     void *app_context)
649 {
650   silc_free(entry->name);
651 }
652
653 /* Foreach callbcak to free all users from the channel when deleting a
654    channel entry. */
655
656 static void silc_idlist_del_channel_foreach(void *key, void *context,
657                                             void *user_context)
658 {
659   SilcChannelClientEntry chl = (SilcChannelClientEntry)context;
660
661   SILC_LOG_DEBUG(("Removing client %s from channel %s",
662                   chl->client->nickname ? chl->client->nickname :
663                   (unsigned char *)"", chl->channel->channel_name));
664
665   /* Remove the context from the client's channel hash table as that
666      table and channel's user_list hash table share this same context. */
667   silc_hash_table_del(chl->client->channels, chl->channel);
668   silc_free(chl);
669 }
670
671 /* Free channel entry.  This free's everything. */
672
673 int silc_idlist_del_channel(SilcIDList id_list, SilcChannelEntry entry)
674 {
675   if (entry) {
676     /* Remove from cache */
677     if (!silc_idcache_del_by_context(id_list->channels, entry, NULL)) {
678       SILC_LOG_DEBUG(("Unknown channel, did not delete"));
679       return FALSE;
680     }
681
682     SILC_LOG_DEBUG(("Deleting channel %s", entry->channel_name));
683
684     /* Free all client entrys from the users list. The silc_hash_table_free
685        will free all the entries so they are not freed at the foreach
686        callback. */
687     silc_hash_table_foreach(entry->user_list, silc_idlist_del_channel_foreach,
688                             NULL);
689     silc_hash_table_free(entry->user_list);
690
691     /* Free data */
692     silc_free(entry->channel_name);
693     silc_free(entry->id);
694     silc_free(entry->topic);
695
696     if (entry->invite_list)
697       silc_hash_table_free(entry->invite_list);
698     if (entry->ban_list)
699       silc_hash_table_free(entry->ban_list);
700
701     if (entry->send_key)
702       silc_cipher_free(entry->send_key);
703     if (entry->receive_key)
704       silc_cipher_free(entry->receive_key);
705     if (entry->key) {
706       memset(entry->key, 0, entry->key_len / 8);
707       silc_free(entry->key);
708     }
709     silc_free(entry->cipher);
710     if (entry->hmac)
711       silc_hmac_free(entry->hmac);
712     silc_free(entry->hmac_name);
713     silc_free(entry->rekey);
714     if (entry->founder_key)
715       silc_pkcs_public_key_free(entry->founder_key);
716     if (entry->channel_pubkeys)
717       silc_hash_table_free(entry->channel_pubkeys);
718
719     memset(entry, 'F', sizeof(*entry));
720     silc_free(entry);
721     return TRUE;
722   }
723
724   return FALSE;
725 }
726
727 /* Finds channel by channel name. Channel names are unique and they
728    are not case-sensitive.  The 'name' must be normalized already. */
729
730 SilcChannelEntry
731 silc_idlist_find_channel_by_name(SilcIDList id_list, char *name,
732                                  SilcIDCacheEntry *ret_entry)
733 {
734   SilcIDCacheEntry id_cache = NULL;
735
736   SILC_LOG_DEBUG(("Channel by name %s", name));
737
738   if (!silc_idcache_find_by_name_one(id_list->channels, name, &id_cache))
739     return NULL;
740
741   if (ret_entry)
742     *ret_entry = id_cache;
743
744   SILC_LOG_DEBUG(("Found"));
745
746   /* Touch channel */
747   ((SilcChannelEntry)id_cache->context)->updated = time(NULL);
748
749   return id_cache->context;
750 }
751
752 /* Finds channel by Channel ID. */
753
754 SilcChannelEntry
755 silc_idlist_find_channel_by_id(SilcIDList id_list, SilcChannelID *id,
756                                SilcIDCacheEntry *ret_entry)
757 {
758   SilcIDCacheEntry id_cache = NULL;
759   SilcChannelEntry channel;
760
761   if (!id)
762     return NULL;
763
764   SILC_LOG_DEBUG(("Channel ID (%s)",
765                   silc_id_render(id, SILC_ID_CHANNEL)));
766
767   if (!silc_idcache_find_by_id_one(id_list->channels, (void *)id, &id_cache))
768     return NULL;
769
770   channel = (SilcChannelEntry)id_cache->context;
771
772   if (ret_entry)
773     *ret_entry = id_cache;
774
775   SILC_LOG_DEBUG(("Found"));
776
777   /* Touch channel */
778   channel->updated = time(NULL);
779
780   return channel;
781 }
782
783 /* Replaces old Channel ID with new one. This is done when router forces
784    normal server to change Channel ID. */
785
786 SilcChannelEntry
787 silc_idlist_replace_channel_id(SilcIDList id_list, SilcChannelID *old_id,
788                                SilcChannelID *new_id)
789 {
790   SilcIDCacheEntry id_cache = NULL;
791   SilcChannelEntry channel;
792   char *name;
793
794   if (!old_id || !new_id)
795     return NULL;
796
797   SILC_LOG_DEBUG(("Replacing Channel ID"));
798
799   if (!silc_idcache_find_by_id_one(id_list->channels, (void *)old_id,
800                                    &id_cache))
801     return NULL;
802
803   channel = (SilcChannelEntry)id_cache->context;
804   name = strdup(id_cache->name);
805
806   /* Remove the old entry and add a new one */
807
808   silc_idcache_del_by_id(id_list->channels, (void *)channel->id, NULL);
809   *channel->id = *new_id;
810   silc_idcache_add(id_list->channels, name, channel->id, channel);
811
812   SILC_LOG_DEBUG(("Replaced"));
813
814   /* Touch channel */
815   channel->updated = time(NULL);
816
817   return channel;
818 }
819
820 /* Returns channels from the ID list. If the `channel_id' is NULL then
821    all channels are returned. */
822
823 SilcChannelEntry *
824 silc_idlist_get_channels(SilcIDList id_list, SilcChannelID *channel_id,
825                          SilcUInt32 *channels_count)
826 {
827   SilcList list;
828   SilcIDCacheEntry id_cache = NULL;
829   SilcChannelEntry *channels = NULL;
830   int i = 0;
831
832   SILC_LOG_DEBUG(("Start"));
833
834   if (!channel_id) {
835     if (!silc_idcache_get_all(id_list->channels, &list))
836       return NULL;
837
838     channels = silc_calloc(silc_list_count(list), sizeof(*channels));
839
840     i = 0;
841     silc_list_start(list);
842     while ((id_cache = silc_list_get(list)))
843       channels[i++] = (SilcChannelEntry)id_cache->context;
844   } else {
845     if (!silc_idcache_find_by_id_one(id_list->channels, channel_id, &id_cache))
846       return NULL;
847
848     i = 1;
849     channels = silc_calloc(1, sizeof(*channels));
850     channels[0] = (SilcChannelEntry)id_cache->context;
851   }
852
853   if (channels_count)
854     *channels_count = i;
855
856   return channels;
857 }