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