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