Removed HTTP server libary, it's available in SRT now
[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   /* 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 }