Preliminary SILC Server 1.1 commit.
[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->hash = idata->hash;
39   data->public_key = idata->public_key;
40   memcpy(data->fingerprint, idata->fingerprint, sizeof(data->fingerprint));
41   data->rekey = idata->rekey;
42   data->last_receive = idata->last_receive;
43   data->last_sent = idata->last_sent;
44   data->status = idata->status;
45   data->created = time(0);      /* Update creation time */
46 }
47
48 /* Free's all data in the common ID entry data structure. */
49
50 void silc_idlist_del_data(void *entry)
51 {
52   SilcIDListData idata = (SilcIDListData)entry;
53
54   if (idata->hash)
55     silc_hash_free(idata->hash);
56   if (idata->public_key)
57     silc_pkcs_public_key_free(idata->public_key);
58
59   idata->hash = NULL;
60   idata->public_key = NULL;
61 }
62
63 /* Purges ID cache */
64
65 SILC_TASK_CALLBACK(silc_idlist_purge)
66 {
67   SilcServer server = app_context;
68   SilcIDListPurge i = (SilcIDListPurge)context;
69
70   SILC_LOG_DEBUG(("Purging cache"));
71
72 #if 0
73   /* XXX */
74   silc_idcache_purge(i->cache);
75   silc_schedule_task_add_timeout(server->schedule, silc_idlist_purge, i,
76                                  i->timeout, 0);
77 #endif
78 }
79
80 /******************************************************************************
81
82                           Server entry functions
83
84 ******************************************************************************/
85
86 /* Add new server entry. This adds the new server entry to ID cache and
87    returns the allocated entry object or NULL on error. This is called
88    when new server connects to us. We also add ourselves to cache with
89    this function. */
90
91 SilcServerEntry
92 silc_idlist_add_server(SilcIDList id_list,
93                        char *server_name, int server_type,
94                        SilcServerID *id, SilcServerEntry router,
95                        void *connection)
96 {
97   SilcServerEntry server;
98   char *server_namec = NULL;
99
100   SILC_LOG_DEBUG(("Adding new server entry"));
101
102   /* Normalize name.  This is cached, original is in server context.  */
103   if (server_name) {
104     server_namec = silc_identifier_check(server_name, strlen(server_name),
105                                          SILC_STRING_UTF8, 256, NULL);
106     if (!server_namec)
107       return NULL;
108   }
109
110   server = silc_calloc(1, sizeof(*server));
111   server->server_name = server_name;
112   server->server_type = server_type;
113   server->id = id;
114   server->router = router;
115   server->connection = connection;
116
117   if (!silc_idcache_add(id_list->servers, server_namec,
118                         (void *)server->id, (void *)server)) {
119     silc_free(server);
120     silc_free(server_namec);
121     return NULL;
122   }
123
124   return server;
125 }
126
127 /* Finds server by Server ID */
128
129 SilcServerEntry
130 silc_idlist_find_server_by_id(SilcIDList id_list, SilcServerID *id,
131                               SilcBool registered, SilcIDCacheEntry *ret_entry)
132 {
133   SilcIDCacheEntry id_cache = NULL;
134   SilcServerEntry server;
135
136   if (!id)
137     return NULL;
138
139   SILC_LOG_DEBUG(("Server ID (%s)",
140                   silc_id_render(id, SILC_ID_SERVER)));
141
142   if (!silc_idcache_find_by_id_one(id_list->servers, (void *)id, &id_cache))
143     return NULL;
144
145   server = (SilcServerEntry)id_cache->context;
146
147   if (server && registered &&
148       !(server->data.status & SILC_IDLIST_STATUS_REGISTERED))
149     return NULL;
150
151   if (ret_entry)
152     *ret_entry = id_cache;
153
154   SILC_LOG_DEBUG(("Found"));
155
156   return server;
157 }
158
159 /* Find server by name.  The 'name' must be normalized already. */
160
161 SilcServerEntry
162 silc_idlist_find_server_by_name(SilcIDList id_list, char *name,
163                                 SilcBool registered, SilcIDCacheEntry *ret_entry)
164 {
165   SilcIDCacheEntry id_cache = NULL;
166   SilcServerEntry server;
167
168   SILC_LOG_DEBUG(("Server by name `%s'", name));
169
170   if (!silc_idcache_find_by_name_one(id_list->servers, name, &id_cache))
171     return NULL;
172
173   server = (SilcServerEntry)id_cache->context;
174
175   if (server && registered &&
176       !(server->data.status & SILC_IDLIST_STATUS_REGISTERED))
177     return NULL;
178
179   if (ret_entry)
180     *ret_entry = id_cache;
181
182   SILC_LOG_DEBUG(("Found"));
183
184   return server;
185 }
186
187 /* Find server by connection parameters, hostname and port */
188
189 SilcServerEntry
190 silc_idlist_find_server_by_conn(SilcIDList id_list, char *hostname,
191                                 int port, SilcBool registered,
192                                 SilcIDCacheEntry *ret_entry)
193 {
194   SilcList list;
195   SilcIDCacheEntry id_cache = NULL;
196   SilcServerEntry server = NULL;
197   SilcPacketStream sock;
198   const char *host = NULL, *ip = NULL;
199
200   SILC_LOG_DEBUG(("Server by hostname %s and port %d", hostname, port));
201
202   if (!silc_idcache_get_all(id_list->servers, &list))
203     return NULL;
204
205   silc_list_start(list);
206   while ((id_cache = silc_list_get(list))) {
207     server = id_cache->context;
208     sock = server->connection;
209
210     if (sock && silc_socket_stream_get_info(sock, NULL, &host, &ip, NULL)) {
211       if (((host && !strcasecmp(host, hostname)) ||
212            (ip && !strcasecmp(ip, hostname))) &&
213           server->id->port == SILC_SWAB_16(port))
214         break;
215     }
216
217     id_cache = NULL;
218     server = NULL;
219   }
220
221   if (server && registered &&
222       !(server->data.status & SILC_IDLIST_STATUS_REGISTERED))
223     return NULL;
224
225   if (ret_entry)
226     *ret_entry = id_cache;
227
228   SILC_LOG_DEBUG(("Found"));
229
230   return server;
231 }
232
233 /* Replaces old Server ID with new one */
234
235 SilcServerEntry
236 silc_idlist_replace_server_id(SilcIDList id_list, SilcServerID *old_id,
237                               SilcServerID *new_id)
238 {
239   SilcIDCacheEntry id_cache = NULL;
240   SilcServerEntry server;
241   char *name;
242
243   if (!old_id || !new_id)
244     return NULL;
245
246   SILC_LOG_DEBUG(("Replacing Server ID"));
247
248   if (!silc_idcache_find_by_id_one(id_list->servers, (void *)old_id, &id_cache))
249     return NULL;
250
251   server = (SilcServerEntry)id_cache->context;
252   name = strdup(id_cache->name);
253
254   /* Remove the old entry and add a new one */
255
256   silc_idcache_del_by_id(id_list->servers, (void *)server->id, NULL);
257   *server->id = *new_id;
258   silc_idcache_add(id_list->servers, name, server->id, server);
259
260   SILC_LOG_DEBUG(("Found"));
261
262   return server;
263 }
264
265 /* Removes and free's server entry from ID list */
266
267 int silc_idlist_del_server(SilcIDList id_list, SilcServerEntry entry)
268 {
269   if (entry) {
270     /* Remove from cache */
271     if (!silc_idcache_del_by_context(id_list->servers, entry, NULL)) {
272       SILC_LOG_DEBUG(("Unknown server, did not delete"));
273       return FALSE;
274     }
275
276     SILC_LOG_DEBUG(("Deleting server %s id %s", entry->server_name ?
277                     entry->server_name : "",
278                     entry->id ?
279                     silc_id_render(entry->id, SILC_ID_SERVER) : ""));
280
281     /* Free data */
282     silc_free(entry->server_name);
283     silc_free(entry->id);
284     silc_free(entry->server_info);
285
286     memset(entry, 'F', sizeof(*entry));
287     silc_free(entry);
288     return TRUE;
289   }
290
291   return FALSE;
292 }
293
294 /******************************************************************************
295
296                           Client entry functions
297
298 ******************************************************************************/
299
300 /* Add new client entry. This adds the client entry to ID cache system
301    and returns the allocated client entry or NULL on error.  This is
302    called when new client connection is accepted to the server. If The
303    `router' is provided then the all server routines assume that the client
304    is not directly connected local client but it has router set and is
305    remote.  If this is the case then `connection' must be NULL.  If, on the
306    other hand, the `connection' is provided then the client is assumed
307    to be directly connected local client and `router' must be NULL. */
308
309 SilcClientEntry
310 silc_idlist_add_client(SilcIDList id_list, char *nickname, char *username,
311                        char *userinfo, SilcClientID *id,
312                        SilcServerEntry router, void *connection)
313 {
314   SilcClientEntry client;
315   char *nicknamec = NULL;
316
317   SILC_LOG_DEBUG(("Adding new client entry"));
318
319   /* Normalize name.  This is cached, original is in client context.  */
320   if (nickname) {
321     nicknamec = silc_identifier_check(nickname, strlen(nickname),
322                                       SILC_STRING_UTF8, 128, NULL);
323     if (!nicknamec)
324       return NULL;
325   }
326
327   /* Check username. */
328   if (username) {
329     char u[128 + 1], h[256 + 1];
330     int ret;
331
332     ret = silc_parse_userfqdn(username, u, sizeof(u), h, sizeof(h));
333     if (!u)
334       return NULL;
335     if (!silc_identifier_verify(u, strlen(u), SILC_STRING_UTF8, 128))
336       return NULL;
337     if (ret > 1 && !silc_identifier_verify(h, strlen(h),
338                                            SILC_STRING_UTF8, 256))
339       return NULL;
340   }
341
342   client = silc_calloc(1, sizeof(*client));
343   client->nickname = nickname;
344   client->username = username ? strdup(username) : NULL;
345   client->userinfo = userinfo;
346   client->id = id;
347   client->router = router;
348   client->connection = connection;
349   client->channels = silc_hash_table_alloc(3, silc_hash_ptr, NULL,
350                                            NULL, NULL, NULL, NULL, TRUE);
351
352   if (!silc_idcache_add(id_list->clients, nicknamec, (void *)client->id,
353                         (void *)client)) {
354     silc_hash_table_free(client->channels);
355     silc_free(client);
356     silc_free(nicknamec);
357     return NULL;
358   }
359
360   return client;
361 }
362
363 /* Free client entry. This free's everything and removes the entry
364    from ID cache. Call silc_idlist_del_data before calling this one. */
365
366 int silc_idlist_del_client(SilcIDList id_list, SilcClientEntry entry)
367 {
368   SILC_LOG_DEBUG(("Start"));
369
370   if (entry) {
371     if (!silc_idcache_del_by_context(id_list->clients, entry, NULL /* XXX */)) {
372       SILC_LOG_DEBUG(("Unknown client, did not delete"));
373       return FALSE;
374     }
375
376     assert(!silc_hash_table_count(entry->channels));
377
378     silc_free(entry->nickname);
379     silc_free(entry->servername);
380     silc_free(entry->username);
381     silc_free(entry->userinfo);
382     silc_free(entry->id);
383     silc_free(entry->attrs);
384     silc_hash_table_free(entry->channels);
385
386     memset(entry, 'F', sizeof(*entry));
387     silc_free(entry);
388
389     return TRUE;
390   }
391
392   return FALSE;
393 }
394
395 /* ID Cache destructor */
396
397 void silc_idlist_client_destructor(SilcIDCache cache,
398                                    SilcIDCacheEntry entry,
399                                    void *dest_context,
400                                    void *app_context)
401 {
402   SilcServer server = app_context;
403   SilcClientEntry client;
404
405   client = (SilcClientEntry)entry->context;
406   if (client) {
407     /* Remove this client from the public key hash list */
408     if (client->data.public_key)
409       silc_hash_table_del_by_context(server->pk_hash,
410                                      client->data.public_key, client);
411
412     assert(!silc_hash_table_count(client->channels));
413     silc_free(client->nickname);
414     silc_free(client->servername);
415     silc_free(client->username);
416     silc_free(client->userinfo);
417     silc_free(client->id);
418     silc_free(client->attrs);
419     if (client->data.public_key)
420       silc_pkcs_public_key_free(client->data.public_key);
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 /*XXX, expire */)) {
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 /* Foreach callbcak to free all users from the channel when deleting a
649    channel entry. */
650
651 static void silc_idlist_del_channel_foreach(void *key, void *context,
652                                             void *user_context)
653 {
654   SilcChannelClientEntry chl = (SilcChannelClientEntry)context;
655
656   SILC_LOG_DEBUG(("Removing client %s from channel %s",
657                   chl->client->nickname ? chl->client->nickname :
658                   (unsigned char *)"", chl->channel->channel_name));
659
660   /* Remove the context from the client's channel hash table as that
661      table and channel's user_list hash table share this same context. */
662   silc_hash_table_del(chl->client->channels, chl->channel);
663   silc_free(chl);
664 }
665
666 /* Free channel entry.  This free's everything. */
667
668 int silc_idlist_del_channel(SilcIDList id_list, SilcChannelEntry entry)
669 {
670   if (entry) {
671     /* Remove from cache */
672     if (!silc_idcache_del_by_context(id_list->channels, entry, NULL)) {
673       SILC_LOG_DEBUG(("Unknown channel, did not delete"));
674       return FALSE;
675     }
676
677     SILC_LOG_DEBUG(("Deleting channel %s", entry->channel_name));
678
679     /* Free all client entrys from the users list. The silc_hash_table_free
680        will free all the entries so they are not freed at the foreach
681        callback. */
682     silc_hash_table_foreach(entry->user_list, silc_idlist_del_channel_foreach,
683                             NULL);
684     silc_hash_table_free(entry->user_list);
685
686     /* Free data */
687     silc_free(entry->channel_name);
688     silc_free(entry->id);
689     silc_free(entry->topic);
690
691     if (entry->invite_list)
692       silc_hash_table_free(entry->invite_list);
693     if (entry->ban_list)
694       silc_hash_table_free(entry->ban_list);
695
696     if (entry->send_key)
697       silc_cipher_free(entry->send_key);
698     if (entry->receive_key)
699       silc_cipher_free(entry->receive_key);
700     if (entry->key) {
701       memset(entry->key, 0, entry->key_len / 8);
702       silc_free(entry->key);
703     }
704     silc_free(entry->cipher);
705     if (entry->hmac)
706       silc_hmac_free(entry->hmac);
707     silc_free(entry->hmac_name);
708     silc_free(entry->rekey);
709     if (entry->founder_key)
710       silc_pkcs_public_key_free(entry->founder_key);
711     if (entry->channel_pubkeys)
712       silc_hash_table_free(entry->channel_pubkeys);
713
714     memset(entry, 'F', sizeof(*entry));
715     silc_free(entry);
716     return TRUE;
717   }
718
719   return FALSE;
720 }
721
722 /* Finds channel by channel name. Channel names are unique and they
723    are not case-sensitive.  The 'name' must be normalized already. */
724
725 SilcChannelEntry
726 silc_idlist_find_channel_by_name(SilcIDList id_list, char *name,
727                                  SilcIDCacheEntry *ret_entry)
728 {
729   SilcIDCacheEntry id_cache = NULL;
730
731   SILC_LOG_DEBUG(("Channel by name %s", name));
732
733   if (!silc_idcache_find_by_name_one(id_list->channels, name, &id_cache))
734     return NULL;
735
736   if (ret_entry)
737     *ret_entry = id_cache;
738
739   SILC_LOG_DEBUG(("Found"));
740
741   /* Touch channel */
742   ((SilcChannelEntry)id_cache->context)->updated = time(NULL);
743
744   return id_cache->context;
745 }
746
747 /* Finds channel by Channel ID. */
748
749 SilcChannelEntry
750 silc_idlist_find_channel_by_id(SilcIDList id_list, SilcChannelID *id,
751                                SilcIDCacheEntry *ret_entry)
752 {
753   SilcIDCacheEntry id_cache = NULL;
754   SilcChannelEntry channel;
755
756   if (!id)
757     return NULL;
758
759   SILC_LOG_DEBUG(("Channel ID (%s)",
760                   silc_id_render(id, SILC_ID_CHANNEL)));
761
762   if (!silc_idcache_find_by_id_one(id_list->channels, (void *)id, &id_cache))
763     return NULL;
764
765   channel = (SilcChannelEntry)id_cache->context;
766
767   if (ret_entry)
768     *ret_entry = id_cache;
769
770   SILC_LOG_DEBUG(("Found"));
771
772   /* Touch channel */
773   channel->updated = time(NULL);
774
775   return channel;
776 }
777
778 /* Replaces old Channel ID with new one. This is done when router forces
779    normal server to change Channel ID. */
780
781 SilcChannelEntry
782 silc_idlist_replace_channel_id(SilcIDList id_list, SilcChannelID *old_id,
783                                SilcChannelID *new_id)
784 {
785   SilcIDCacheEntry id_cache = NULL;
786   SilcChannelEntry channel;
787   char *name;
788
789   if (!old_id || !new_id)
790     return NULL;
791
792   SILC_LOG_DEBUG(("Replacing Channel ID"));
793
794   if (!silc_idcache_find_by_id_one(id_list->channels, (void *)old_id,
795                                    &id_cache))
796     return NULL;
797
798   channel = (SilcChannelEntry)id_cache->context;
799   name = strdup(id_cache->name);
800
801   /* Remove the old entry and add a new one */
802
803   silc_idcache_del_by_id(id_list->channels, (void *)channel->id, NULL);
804   *channel->id = *new_id;
805   silc_idcache_add(id_list->channels, name, channel->id, channel);
806
807   SILC_LOG_DEBUG(("Replaced"));
808
809   /* Touch channel */
810   channel->updated = time(NULL);
811
812   return channel;
813 }
814
815 /* Returns channels from the ID list. If the `channel_id' is NULL then
816    all channels are returned. */
817
818 SilcChannelEntry *
819 silc_idlist_get_channels(SilcIDList id_list, SilcChannelID *channel_id,
820                          SilcUInt32 *channels_count)
821 {
822   SilcList list;
823   SilcIDCacheEntry id_cache = NULL;
824   SilcChannelEntry *channels = NULL;
825   int i = 0;
826
827   SILC_LOG_DEBUG(("Start"));
828
829   if (!channel_id) {
830     if (!silc_idcache_get_all(id_list->channels, &list))
831       return NULL;
832
833     channels = silc_calloc(silc_list_count(list), sizeof(*channels));
834
835     i = 0;
836     silc_list_start(list);
837     while ((id_cache = silc_list_get(list)))
838       channels[i++] = (SilcChannelEntry)id_cache->context;
839   } else {
840     if (!silc_idcache_find_by_id_one(id_list->channels, channel_id, &id_cache))
841       return NULL;
842
843     i = 1;
844     channels = silc_calloc(1, sizeof(*channels));
845     channels[0] = (SilcChannelEntry)id_cache->context;
846   }
847
848   if (channels_count)
849     *channels_count = i;
850
851   return channels;
852 }