updates.
[silc.git] / lib / silcclient / idlist.c
1 /*
2
3   idlist.c 
4
5   Author: Pekka Riikonen <priikone@silcnet.org>
6
7   Copyright (C) 2001 - 2002 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; version 2 of the License.
12
13   This program is distributed in the hope that it will be useful,
14   but WITHOUT ANY WARRANTY; without even the implied warranty of
15   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16   GNU General Public License for more details.
17
18 */
19 /* $Id$ */
20
21 #include "clientlibincludes.h"
22 #include "client_internal.h"
23
24 /******************************************************************************
25
26                          Client Searching Locally
27
28 ******************************************************************************/
29
30 /* Same as silc_client_get_clients function but does not resolve anything
31    from the server. This checks local cache and returns all matching
32    clients from the local cache. If none was found this returns NULL.
33    The `nickname' is the real nickname of the client, and the `format'
34    is the formatted nickname to find exact match from multiple found
35    entries. The format must be same as given in the SilcClientParams
36    structure to the client library. If the `format' is NULL all found
37    clients by `nickname' are returned. */
38
39 SilcClientEntry *silc_client_get_clients_local(SilcClient client,
40                                                SilcClientConnection conn,
41                                                const char *nickname,
42                                                const char *format,
43                                                uint32 *clients_count)
44 {
45   SilcIDCacheEntry id_cache;
46   SilcIDCacheList list = NULL;
47   SilcClientEntry entry, *clients;
48   int i = 0;
49   bool found = FALSE;
50
51   /* Find ID from cache */
52   if (!silc_idcache_find_by_name(conn->client_cache, (char *)nickname, &list))
53     return NULL;
54
55   if (!silc_idcache_list_count(list)) {
56     silc_idcache_list_free(list);
57     return NULL;
58   }
59
60   clients = silc_calloc(silc_idcache_list_count(list), sizeof(*clients));
61   *clients_count = silc_idcache_list_count(list);
62
63   if (!format) {
64     /* Take all without any further checking */
65     silc_idcache_list_first(list, &id_cache);
66     while (id_cache) {
67       clients[i++] = id_cache->context;
68       found = TRUE;
69       if (!silc_idcache_list_next(list, &id_cache))
70         break;
71     }
72   } else {
73     /* Check multiple cache entries for match */
74     silc_idcache_list_first(list, &id_cache);
75     while (id_cache) {
76       entry = (SilcClientEntry)id_cache->context;
77       if (strcasecmp(entry->nickname, format)) {
78         if (!silc_idcache_list_next(list, &id_cache)) {
79           break;
80         } else {
81           continue;
82         }
83       }
84       
85       clients[i++] = id_cache->context;
86       found = TRUE;
87       if (!silc_idcache_list_next(list, &id_cache))
88         break;
89     }
90   }
91
92   if (list)
93     silc_idcache_list_free(list);
94
95   if (!found) {
96     *clients_count = 0;
97     if (clients)
98       silc_free(clients);
99     return NULL;
100   }
101
102   return clients;
103 }
104
105
106 /******************************************************************************
107
108                         Client Resolving from Server
109
110 ******************************************************************************/
111
112 typedef struct {
113   SilcClient client;
114   SilcClientConnection conn;
115   SilcGetClientCallback completion;
116   void *context;
117   char *nickname;
118   char *server;
119 } *GetClientInternal;
120
121 SILC_CLIENT_CMD_FUNC(get_client_callback)
122 {
123   GetClientInternal i = (GetClientInternal)context;
124   SilcClientEntry *clients;
125   uint32 clients_count;
126
127   /* Get the clients */
128   clients = silc_client_get_clients_local(i->client, i->conn,
129                                           i->nickname, i->server,
130                                           &clients_count);
131   if (clients) {
132     i->completion(i->client, i->conn, clients, clients_count, i->context);
133     silc_free(clients);
134   } else {
135     i->completion(i->client, i->conn, NULL, 0, i->context);
136   }
137
138   silc_free(i->nickname);
139   silc_free(i->server);
140   silc_free(i);
141 }
142
143 /* Finds client entry or entries by the `nickname' and `server'. The 
144    completion callback will be called when the client entries has been found.
145
146    Note: this function is always asynchronous and resolves the client
147    information from the server. Thus, if you already know the client
148    information then use the silc_client_get_client_by_id function to
149    get the client entry since this function may be very slow and should
150    be used only to initially get the client entries. */
151
152 void silc_client_get_clients(SilcClient client,
153                              SilcClientConnection conn,
154                              const char *nickname,
155                              const char *server,
156                              SilcGetClientCallback completion,
157                              void *context)
158 {
159   GetClientInternal i;
160   char *userhost;
161
162   if (!nickname)
163     return;
164
165   i = silc_calloc(1, sizeof(*i));
166   i->client = client;
167   i->conn = conn;
168   i->nickname = strdup(nickname);
169   i->server = server ? strdup(server) : NULL;
170   i->completion = completion;
171   i->context = context;
172
173   if (nickname && server) {
174     userhost = silc_calloc(strlen(nickname) + strlen(server) + 2,
175                            sizeof(*userhost));
176     strncat(userhost, nickname, strlen(nickname));
177     strncat(userhost, "@", 1);
178     strncat(userhost, server, strlen(server));
179   } else {
180     userhost = strdup(nickname);
181   }
182
183   /* Register our own command reply for this command */
184   silc_client_command_register(client, SILC_COMMAND_IDENTIFY, NULL, NULL,
185                                silc_client_command_reply_identify_i, 0,
186                                ++conn->cmd_ident);
187
188   /* Send the command */
189   silc_client_command_send(client, conn, SILC_COMMAND_IDENTIFY, 
190                            conn->cmd_ident, 1, 1, userhost, 
191                            strlen(userhost));
192
193   /* Add pending callback */
194   silc_client_command_pending(conn, SILC_COMMAND_IDENTIFY, conn->cmd_ident,
195                               silc_client_command_get_client_callback, 
196                               (void *)i);
197
198   silc_free(userhost);
199 }
200
201 /* The old style function to find client entry. This is used by the
202    library internally. If `query' is TRUE then the client information is
203    requested by the server. The pending command callback must be set
204    by the caller. */
205 /* XXX This function should be removed */
206
207 SilcClientEntry silc_idlist_get_client(SilcClient client,
208                                        SilcClientConnection conn,
209                                        const char *nickname,
210                                        const char *format,
211                                        bool query)
212 {
213   SilcIDCacheEntry id_cache;
214   SilcIDCacheList list = NULL;
215   SilcClientEntry entry = NULL;
216
217   SILC_LOG_DEBUG(("Start"));
218
219   /* Find ID from cache */
220   if (!silc_idcache_find_by_name(conn->client_cache, (char *)nickname, 
221                                  &list)) {
222   identify:
223
224     if (query) {
225       SILC_LOG_DEBUG(("Requesting Client ID from server"));
226       
227       /* Register our own command reply for this command */
228       silc_client_command_register(client, SILC_COMMAND_IDENTIFY, NULL, NULL,
229                                    silc_client_command_reply_identify_i, 0,
230                                    ++conn->cmd_ident);
231
232       /* Send the command */
233       silc_client_command_send(client, conn, SILC_COMMAND_IDENTIFY, 
234                                conn->cmd_ident, 1, 1, nickname,
235                                strlen(nickname));
236
237       if (list)
238         silc_idcache_list_free(list);
239
240       return NULL;
241     }
242     return NULL;
243   }
244
245   if (!format) {
246     /* Take first found cache entry */
247     if (!silc_idcache_list_first(list, &id_cache))
248       goto identify;
249
250     entry = (SilcClientEntry)id_cache->context;
251   } else {
252     /* Check multiple cache entries for match */
253     silc_idcache_list_first(list, &id_cache);
254     while (id_cache) {
255       entry = (SilcClientEntry)id_cache->context;
256
257       if (strcasecmp(entry->nickname, format)) {
258         if (!silc_idcache_list_next(list, &id_cache)) {
259           entry = NULL;
260           break;
261         } else {
262           entry = NULL;
263           continue;
264         }
265       }
266
267       break;
268     }
269
270     /* If match weren't found, request it */
271     if (!entry)
272       goto identify;
273   }
274
275   if (list)
276     silc_idcache_list_free(list);
277
278   return entry;
279 }
280
281
282 typedef struct {
283   SilcClient client;
284   SilcClientConnection conn;
285   uint32 list_count;
286   SilcBuffer client_id_list;
287   SilcGetClientCallback completion;
288   void *context;
289 } *GetClientsByListInternal;
290
291 SILC_CLIENT_CMD_FUNC(get_clients_list_callback)
292 {
293   GetClientsByListInternal i = (GetClientsByListInternal)context;
294   SilcIDCacheEntry id_cache = NULL;
295   SilcBuffer client_id_list = i->client_id_list;
296   SilcClientEntry *clients = NULL;
297   uint32 clients_count = 0;
298   bool found = FALSE;
299   int c;
300
301   SILC_LOG_DEBUG(("Start"));
302
303   for (c = 0; c < i->list_count; c++) {
304     uint16 idp_len;
305     SilcClientID *client_id;
306
307     /* Get Client ID */
308     SILC_GET16_MSB(idp_len, client_id_list->data + 2);
309     idp_len += 4;
310     client_id = silc_id_payload_parse_id(client_id_list->data, idp_len);
311     if (!client_id) {
312       silc_buffer_pull(client_id_list, idp_len);
313       continue;
314     }
315
316     /* Get the client entry */
317     if (silc_idcache_find_by_id_one_ext(i->conn->client_cache, 
318                                         (void *)client_id, 
319                                         NULL, NULL, 
320                                         silc_hash_client_id_compare, NULL,
321                                         &id_cache)) {
322       clients = silc_realloc(clients, sizeof(*clients) * 
323                              (clients_count + 1));
324       clients[clients_count] = (SilcClientEntry)id_cache->context;
325       clients_count++;
326       found = TRUE;
327     }
328
329     silc_free(client_id);
330     silc_buffer_pull(client_id_list, idp_len);
331   }
332
333   if (found) {
334     i->completion(i->client, i->conn, clients, clients_count, i->context);
335     silc_free(clients);
336   } else {
337     i->completion(i->client, i->conn, NULL, 0, i->context);
338   }
339
340   if (i->client_id_list)
341     silc_buffer_free(i->client_id_list);
342   silc_free(i);
343 }
344
345 /* Gets client entries by the list of client ID's `client_id_list'. This
346    always resolves those client ID's it does not know yet from the server
347    so this function might take a while. The `client_id_list' is a list
348    of ID Payloads added one after other.  JOIN command reply and USERS
349    command reply for example returns this sort of list. The `completion'
350    will be called after the entries are available. */
351
352 void silc_client_get_clients_by_list(SilcClient client,
353                                      SilcClientConnection conn,
354                                      uint32 list_count,
355                                      SilcBuffer client_id_list,
356                                      SilcGetClientCallback completion,
357                                      void *context)
358 {
359   SilcIDCacheEntry id_cache = NULL;
360   int i;
361   unsigned char **res_argv = NULL;
362   uint32 *res_argv_lens = NULL, *res_argv_types = NULL, res_argc = 0;
363   GetClientsByListInternal in;
364
365   SILC_LOG_DEBUG(("Start"));
366
367   in = silc_calloc(1, sizeof(*in));
368   in->client = client;
369   in->conn = conn;
370   in->list_count = list_count;
371   in->client_id_list = silc_buffer_copy(client_id_list);
372   in->completion = completion;
373   in->context = context;
374
375   for (i = 0; i < list_count; i++) {
376     uint16 idp_len;
377     SilcClientID *client_id;
378     SilcClientEntry entry;
379
380     /* Get Client ID */
381     SILC_GET16_MSB(idp_len, client_id_list->data + 2);
382     idp_len += 4;
383     client_id = silc_id_payload_parse_id(client_id_list->data, idp_len);
384     if (!client_id) {
385       silc_buffer_pull(client_id_list, idp_len);
386       continue;
387     }
388
389     /* Check if we have this client cached already. */
390     id_cache = NULL;
391     silc_idcache_find_by_id_one_ext(conn->client_cache, (void *)client_id, 
392                                     NULL, NULL, 
393                                     silc_hash_client_id_compare, NULL,
394                                     &id_cache);
395
396     /* If we don't have the entry or it has incomplete info, then resolve
397        it from the server. */
398     entry = id_cache ? (SilcClientEntry)id_cache->context : NULL;
399     if (!id_cache || !entry->nickname) {
400
401       if (entry) {
402         if (entry->status & SILC_CLIENT_STATUS_RESOLVING) {
403           entry->status &= ~SILC_CLIENT_STATUS_RESOLVING;
404           silc_free(client_id);
405           silc_buffer_pull(client_id_list, idp_len);
406           continue;
407         }
408
409         entry->status |= SILC_CLIENT_STATUS_RESOLVING;
410       }
411
412       /* No we don't have it, query it from the server. Assemble argument
413          table that will be sent fr the IDENTIFY command later. */
414       res_argv = silc_realloc(res_argv, sizeof(*res_argv) *
415                               (res_argc + 1));
416       res_argv_lens = silc_realloc(res_argv_lens, sizeof(*res_argv_lens) *
417                                    (res_argc + 1));
418       res_argv_types = silc_realloc(res_argv_types, sizeof(*res_argv_types) *
419                                     (res_argc + 1));
420       res_argv[res_argc] = client_id_list->data;
421       res_argv_lens[res_argc] = idp_len;
422       res_argv_types[res_argc] = res_argc + 5;
423       res_argc++;
424     }
425
426     silc_free(client_id);
427     silc_buffer_pull(client_id_list, idp_len);
428   }
429
430   /* Query the client information from server if the list included clients
431      that we don't know about. */
432   if (res_argc) {
433     SilcBuffer res_cmd;
434
435     /* Send the IDENTIFY command to server */
436     res_cmd = silc_command_payload_encode(SILC_COMMAND_IDENTIFY,
437                                           res_argc, res_argv, res_argv_lens,
438                                           res_argv_types, ++conn->cmd_ident);
439     silc_client_packet_send(client, conn->sock, SILC_PACKET_COMMAND, 
440                             NULL, 0, NULL, NULL, res_cmd->data, res_cmd->len,
441                             TRUE);
442
443     /* Register our own command reply for this command */
444     silc_client_command_register(client, SILC_COMMAND_IDENTIFY, NULL, NULL,
445                                  silc_client_command_reply_identify_i, 0,
446                                  conn->cmd_ident);
447
448     /* Process the applications request after reply has been received  */
449     silc_client_command_pending(conn, SILC_COMMAND_IDENTIFY, conn->cmd_ident,
450                                 silc_client_command_get_clients_list_callback, 
451                                 (void *)in);
452
453     silc_buffer_push(client_id_list, client_id_list->data - 
454                      client_id_list->head);
455     silc_buffer_free(res_cmd);
456     silc_free(res_argv);
457     silc_free(res_argv_lens);
458     silc_free(res_argv_types);
459     return;
460   }
461
462   silc_buffer_push(client_id_list, client_id_list->data - 
463                    client_id_list->head);
464
465   /* We have the clients in cache, get them and call the completion */
466   silc_client_command_get_clients_list_callback((void *)in, NULL);
467 }
468
469 /* Finds entry for client by the client's ID. Returns the entry or NULL
470    if the entry was not found. */
471
472 SilcClientEntry silc_client_get_client_by_id(SilcClient client,
473                                              SilcClientConnection conn,
474                                              SilcClientID *client_id)
475 {
476   SilcIDCacheEntry id_cache;
477
478   SILC_LOG_DEBUG(("Finding client by ID (%s)", 
479                   silc_id_render(client_id, SILC_ID_CLIENT)));
480
481   /* Find ID from cache */
482   if (!silc_idcache_find_by_id_one_ext(conn->client_cache, (void *)client_id, 
483                                        NULL, NULL, 
484                                        silc_hash_client_id_compare, NULL,
485                                        &id_cache))
486     return NULL;
487
488   SILC_LOG_DEBUG(("Found"));
489
490   return (SilcClientEntry)id_cache->context;
491 }
492
493 typedef struct {
494   SilcClient client;
495   SilcClientConnection conn;
496   SilcClientID *client_id;
497   SilcGetClientCallback completion;
498   void *context;
499 } *GetClientByIDInternal;
500
501 SILC_CLIENT_CMD_FUNC(get_client_by_id_callback)
502 {
503   GetClientByIDInternal i = (GetClientByIDInternal)context;
504   SilcClientEntry entry;
505
506   /* Get the client */
507   entry = silc_client_get_client_by_id(i->client, i->conn, i->client_id);
508   if (entry)
509     i->completion(i->client, i->conn, &entry, 1, i->context);
510   else
511     i->completion(i->client, i->conn, NULL, 0, i->context);
512
513   silc_free(i->client_id);
514   silc_free(i);
515 }
516
517 /* Same as above but will always resolve the information from the server.
518    Use this only if you know that you don't have the entry and the only
519    thing you know about the client is its ID. */
520
521 void silc_client_get_client_by_id_resolve(SilcClient client,
522                                           SilcClientConnection conn,
523                                           SilcClientID *client_id,
524                                           SilcGetClientCallback completion,
525                                           void *context)
526 {
527   SilcBuffer idp;
528   GetClientByIDInternal i = silc_calloc(1, sizeof(*i));
529
530   SILC_LOG_DEBUG(("Start"));
531
532   i->client = client;
533   i->conn = conn;
534   i->client_id = silc_id_dup(client_id, SILC_ID_CLIENT);
535   i->completion = completion;
536   i->context = context;
537       
538   /* Register our own command reply for this command */
539   silc_client_command_register(client, SILC_COMMAND_WHOIS, NULL, NULL,
540                                silc_client_command_reply_whois_i, 0,
541                                ++conn->cmd_ident);
542
543   /* Send the command */
544   idp = silc_id_payload_encode(client_id, SILC_ID_CLIENT);
545   silc_client_command_send(client, conn, SILC_COMMAND_WHOIS, conn->cmd_ident,
546                            1, 3, idp->data, idp->len);
547   silc_buffer_free(idp);
548
549   /* Add pending callback */
550   silc_client_command_pending(conn, SILC_COMMAND_WHOIS, conn->cmd_ident,
551                               silc_client_command_get_client_by_id_callback, 
552                               (void *)i);
553 }
554
555
556 /******************************************************************************
557
558                 Client, Channel and Server entry manipulation
559
560 ******************************************************************************/
561
562
563 /* Creates new client entry and adds it to the ID cache. Returns pointer
564    to the new entry. */
565
566 SilcClientEntry
567 silc_client_add_client(SilcClient client, SilcClientConnection conn,
568                        char *nickname, char *username, 
569                        char *userinfo, SilcClientID *id, uint32 mode)
570 {
571   SilcClientEntry client_entry;
572   char *nick = NULL;
573
574   SILC_LOG_DEBUG(("Start"));
575
576   /* Save the client infos */
577   client_entry = silc_calloc(1, sizeof(*client_entry));
578   client_entry->id = id;
579   client_entry->valid = TRUE;
580   silc_parse_userfqdn(nickname, &nick, &client_entry->server);
581   silc_parse_userfqdn(username, &client_entry->username, 
582                       &client_entry->hostname);
583   if (userinfo)
584     client_entry->realname = strdup(userinfo);
585   client_entry->mode = mode;
586   if (nick)
587     client_entry->nickname = strdup(nick);
588   client_entry->channels = silc_hash_table_alloc(1, silc_hash_ptr, NULL, NULL,
589                                                  NULL, NULL, NULL, TRUE);
590
591   /* Format the nickname */
592   silc_client_nickname_format(client, conn, client_entry);
593   
594   /* Add client to cache, the non-formatted nickname is saved to cache */
595   if (!silc_idcache_add(conn->client_cache, nick, client_entry->id, 
596                         (void *)client_entry, 0, NULL)) {
597     silc_free(client_entry->nickname);
598     silc_free(client_entry->username);
599     silc_free(client_entry->hostname);
600     silc_free(client_entry->server);
601     silc_hash_table_free(client_entry->channels);
602     silc_free(client_entry);
603     return NULL;
604   }
605
606   return client_entry;
607 }
608
609 /* Updates the `client_entry' with the new information sent as argument. */
610
611 void silc_client_update_client(SilcClient client,
612                                SilcClientConnection conn,
613                                SilcClientEntry client_entry,
614                                const char *nickname,
615                                const char *username,
616                                const char *userinfo,
617                                uint32 mode)
618 {
619   char *nick = NULL;
620
621   SILC_LOG_DEBUG(("Start"));
622
623   if (!client_entry->username && username)
624     silc_parse_userfqdn(username, &client_entry->username, 
625                         &client_entry->hostname);
626   if (!client_entry->realname && userinfo)
627     client_entry->realname = strdup(userinfo);
628   if (!client_entry->nickname && nickname) {
629     silc_parse_userfqdn(nickname, &nick, &client_entry->server);
630     client_entry->nickname = strdup(nick);
631     silc_client_nickname_format(client, conn, client_entry);
632   }
633   client_entry->mode = mode;
634
635   if (nick) {
636     /* Remove the old cache entry and create a new one */
637     silc_idcache_del_by_context(conn->client_cache, client_entry);
638     silc_idcache_add(conn->client_cache, nick, client_entry->id, 
639                      client_entry, 0, NULL);
640   }
641 }
642
643 /* Deletes the client entry and frees all memory. */
644
645 void silc_client_del_client_entry(SilcClient client, 
646                                   SilcClientConnection conn,
647                                   SilcClientEntry client_entry)
648 {
649   SILC_LOG_DEBUG(("Start"));
650
651   silc_free(client_entry->nickname);
652   silc_free(client_entry->username);
653   silc_free(client_entry->realname);
654   silc_free(client_entry->hostname);
655   silc_free(client_entry->server);
656   silc_free(client_entry->id);
657   silc_free(client_entry->fingerprint);
658   silc_hash_table_free(client_entry->channels);
659   if (client_entry->send_key)
660     silc_cipher_free(client_entry->send_key);
661   if (client_entry->receive_key)
662     silc_cipher_free(client_entry->receive_key);
663   silc_free(client_entry->key);
664   silc_client_ftp_session_free_client(conn, client_entry);
665   if (client_entry->ke)
666     silc_client_abort_key_agreement(client, conn, client_entry);
667   silc_free(client_entry);
668 }
669
670 /* Removes client from the cache by the client entry. */
671
672 bool silc_client_del_client(SilcClient client, SilcClientConnection conn,
673                             SilcClientEntry client_entry)
674 {
675   bool ret = silc_idcache_del_by_context(conn->client_cache, client_entry);
676
677   /* Remove from channels */
678   silc_client_remove_from_channels(client, conn, client_entry);
679
680   /* Free the client entry data */
681   silc_client_del_client_entry(client, conn, client_entry);
682
683   return ret;
684 }
685
686 /* Add new channel entry to the ID Cache */
687
688 SilcChannelEntry silc_client_add_channel(SilcClient client,
689                                          SilcClientConnection conn,
690                                          const char *channel_name,
691                                          uint32 mode, 
692                                          SilcChannelID *channel_id)
693 {
694   SilcChannelEntry channel;
695
696   SILC_LOG_DEBUG(("Start"));
697
698   channel = silc_calloc(1, sizeof(*channel));
699   channel->channel_name = strdup(channel_name);
700   channel->id = channel_id;
701   channel->mode = mode;
702   channel->user_list = silc_hash_table_alloc(1, silc_hash_ptr, NULL, NULL,
703                                              NULL, NULL, NULL, TRUE);
704
705   /* Put it to the ID cache */
706   if (!silc_idcache_add(conn->channel_cache, channel->channel_name, 
707                         (void *)channel->id, (void *)channel, 0, NULL)) {
708     silc_free(channel->channel_name);
709     silc_hash_table_free(channel->user_list);
710     silc_free(channel);
711     return NULL;
712   }
713
714   return channel;
715 }
716
717 /* Foreach callbcak to free all users from the channel when deleting a
718    channel entry. */
719
720 static void silc_client_del_channel_foreach(void *key, void *context,
721                                             void *user_context)
722 {
723   SilcChannelUser chu = (SilcChannelUser)context;
724
725   /* Remove the context from the client's channel hash table as that
726      table and channel's user_list hash table share this same context. */
727   silc_hash_table_del(chu->client->channels, chu->channel);
728   silc_free(chu);
729 }
730
731 /* Removes channel from the cache by the channel entry. */
732
733 bool silc_client_del_channel(SilcClient client, SilcClientConnection conn,
734                              SilcChannelEntry channel)
735 {
736   bool ret = silc_idcache_del_by_context(conn->channel_cache, channel);
737
738   /* Free all client entrys from the users list. The silc_hash_table_free
739      will free all the entries so they are not freed at the foreach 
740      callback. */
741   silc_hash_table_foreach(channel->user_list, silc_client_del_channel_foreach,
742                           NULL);
743   silc_hash_table_free(channel->user_list);
744
745   silc_free(channel->channel_name);
746   silc_free(channel->id);
747   silc_free(channel->key);
748   if (channel->channel_key)
749     silc_cipher_free(channel->channel_key);
750   if (channel->hmac)
751     silc_hmac_free(channel->hmac);
752   if (channel->old_channel_key)
753     silc_cipher_free(channel->old_channel_key);
754   if (channel->old_hmac)
755     silc_hmac_free(channel->old_hmac);
756   if (channel->rekey_task)
757     silc_schedule_task_del(conn->client->schedule, channel->rekey_task);
758   silc_client_del_channel_private_keys(client, conn, channel);
759   silc_free(channel);
760   return ret;
761 }
762
763 /* Replaces the channel ID of the `channel' to `new_id'. Returns FALSE
764    if the ID could not be changed. */
765
766 bool silc_client_replace_channel_id(SilcClient client,
767                                     SilcClientConnection conn,
768                                     SilcChannelEntry channel,
769                                     SilcChannelID *new_id)
770 {
771   if (!new_id)
772     return FALSE;
773
774   SILC_LOG_DEBUG(("Old Channel ID id(%s)", 
775                   silc_id_render(channel->id, SILC_ID_CHANNEL)));
776   SILC_LOG_DEBUG(("New Channel ID id(%s)", 
777                   silc_id_render(new_id, SILC_ID_CHANNEL)));
778
779   silc_idcache_del_by_id(conn->channel_cache, channel->id);
780   silc_free(channel->id);
781   channel->id = new_id;
782   return silc_idcache_add(conn->channel_cache, channel->channel_name, 
783                           (void *)channel->id, (void *)channel, 0, NULL);
784
785 }
786
787 /* Finds entry for channel by the channel name. Returns the entry or NULL
788    if the entry was not found. It is found only if the client is joined
789    to the channel. */
790
791 SilcChannelEntry silc_client_get_channel(SilcClient client,
792                                          SilcClientConnection conn,
793                                          char *channel)
794 {
795   SilcIDCacheEntry id_cache;
796   SilcChannelEntry entry;
797
798   SILC_LOG_DEBUG(("Start"));
799
800   if (!silc_idcache_find_by_name_one(conn->channel_cache, channel, 
801                                      &id_cache))
802     return NULL;
803
804   entry = (SilcChannelEntry)id_cache->context;
805
806   SILC_LOG_DEBUG(("Found"));
807
808   return entry;
809 }
810
811 /* Finds entry for channel by the channel ID. Returns the entry or NULL
812    if the entry was not found. It is found only if the client is joined
813    to the channel. */
814
815 SilcChannelEntry silc_client_get_channel_by_id(SilcClient client,
816                                                SilcClientConnection conn,
817                                                SilcChannelID *channel_id)
818 {
819   SilcIDCacheEntry id_cache;
820   SilcChannelEntry entry;
821
822   SILC_LOG_DEBUG(("Start"));
823
824   if (!silc_idcache_find_by_id_one(conn->channel_cache, channel_id, 
825                                    &id_cache))
826     return NULL;
827
828   entry = (SilcChannelEntry)id_cache->context;
829
830   SILC_LOG_DEBUG(("Found"));
831
832   return entry;
833 }
834
835 typedef struct {
836   SilcClient client;
837   SilcClientConnection conn;
838   SilcChannelID *channel_id;
839   SilcGetChannelCallback completion;
840   void *context;
841 } *GetChannelByIDInternal;
842
843 SILC_CLIENT_CMD_FUNC(get_channel_by_id_callback)
844 {
845   GetChannelByIDInternal i = (GetChannelByIDInternal)context;
846   SilcChannelEntry entry;
847
848   SILC_LOG_DEBUG(("Start"));
849
850   /* Get the channel */
851   entry = silc_client_get_channel_by_id(i->client, i->conn, i->channel_id);
852   if (entry) {
853     i->completion(i->client, i->conn, &entry, 1, i->context);
854   } else {
855     i->completion(i->client, i->conn, NULL, 0, i->context);
856   }
857
858   silc_free(i->channel_id);
859   silc_free(i);
860 }
861
862 /* Resolves channel information from the server by the channel ID. */
863
864 void silc_client_get_channel_by_id_resolve(SilcClient client,
865                                            SilcClientConnection conn,
866                                            SilcChannelID *channel_id,
867                                            SilcGetChannelCallback completion,
868                                            void *context)
869 {
870   SilcBuffer idp;
871   GetChannelByIDInternal i = silc_calloc(1, sizeof(*i));
872
873   SILC_LOG_DEBUG(("Start"));
874
875   i->client = client;
876   i->conn = conn;
877   i->channel_id = silc_id_dup(channel_id, SILC_ID_CHANNEL);
878   i->completion = completion;
879   i->context = context;
880       
881   /* Register our own command reply for this command */
882   silc_client_command_register(client, SILC_COMMAND_IDENTIFY, NULL, NULL,
883                                silc_client_command_reply_identify_i, 0,
884                                ++conn->cmd_ident);
885
886   /* Send the command */
887   idp = silc_id_payload_encode(channel_id, SILC_ID_CHANNEL);
888   silc_client_command_send(client, conn, SILC_COMMAND_IDENTIFY, 
889                            conn->cmd_ident,
890                            1, 5, idp->data, idp->len);
891   silc_buffer_free(idp);
892
893   /* Add pending callback */
894   silc_client_command_pending(conn, SILC_COMMAND_IDENTIFY, conn->cmd_ident,
895                               silc_client_command_get_channel_by_id_callback, 
896                               (void *)i);
897 }
898
899 /* Finds entry for server by the server name. */
900
901 SilcServerEntry silc_client_get_server(SilcClient client,
902                                        SilcClientConnection conn,
903                                        char *server_name)
904 {
905   SilcIDCacheEntry id_cache;
906   SilcServerEntry entry;
907
908   SILC_LOG_DEBUG(("Start"));
909
910   if (!silc_idcache_find_by_name_one(conn->server_cache, server_name, 
911                                      &id_cache))
912     return NULL;
913
914   entry = (SilcServerEntry)id_cache->context;
915
916   return entry;
917 }
918
919 /* Finds entry for server by the server ID. */
920
921 SilcServerEntry silc_client_get_server_by_id(SilcClient client,
922                                              SilcClientConnection conn,
923                                              SilcServerID *server_id)
924 {
925   SilcIDCacheEntry id_cache;
926   SilcServerEntry entry;
927
928   SILC_LOG_DEBUG(("Start"));
929
930   if (!silc_idcache_find_by_id_one(conn->server_cache, (void *)server_id, 
931                                    &id_cache))
932     return NULL;
933
934   entry = (SilcServerEntry)id_cache->context;
935
936   return entry;
937 }
938
939 /* Removes server from the cache by the server entry. */
940
941 bool silc_client_del_server(SilcClient client, SilcClientConnection conn,
942                             SilcServerEntry server)
943 {
944   bool ret = silc_idcache_del_by_context(conn->server_cache, server);
945   silc_free(server->server_name);
946   silc_free(server->server_info);
947   silc_free(server->server_id);
948   silc_free(server);
949   return ret;
950 }
951
952 /* Formats the nickname of the client specified by the `client_entry'.
953    If the format is specified by the application this will format the
954    nickname and replace the old nickname in the client entry. If the
955    format string is not specified then this function has no effect. */
956
957 void silc_client_nickname_format(SilcClient client, 
958                                  SilcClientConnection conn,
959                                  SilcClientEntry client_entry)
960 {
961   char *cp;
962   char *newnick = NULL;
963   int i, off = 0, len;
964   SilcClientEntry *clients;
965   uint32 clients_count = 0;
966   SilcClientEntry unformatted = NULL;
967
968   SILC_LOG_DEBUG(("Start"));
969
970   if (!client->internal->params->nickname_format[0])
971     return;
972
973   if (!client_entry->nickname)
974     return;
975
976   /* Get all clients with same nickname. Do not perform the formatting
977      if there aren't any clients with same nickname unless the application
978      is forcing us to do so. */
979   clients = silc_client_get_clients_local(client, conn,
980                                           client_entry->nickname, NULL,
981                                           &clients_count);
982   if (!clients && !client->internal->params->nickname_force_format)
983     return;
984
985   len = 0;
986   for (i = 0; i < clients_count; i++)
987     if (clients[i]->valid && clients[i] != client_entry)
988       len++;
989   if (!len)
990     return;
991
992   cp = client->internal->params->nickname_format;
993   while (*cp) {
994     if (*cp == '%') {
995       cp++;
996       continue;
997     }
998
999     switch(*cp) {
1000     case 'n':
1001       /* Nickname */
1002       if (!client_entry->nickname)
1003         break;
1004       len = strlen(client_entry->nickname);
1005       newnick = silc_realloc(newnick, sizeof(*newnick) * (off + len));
1006       memcpy(&newnick[off], client_entry->nickname, len);
1007       off += len;
1008       break;
1009     case 'h':
1010       /* Stripped hostname */
1011       if (!client_entry->hostname)
1012         break;
1013       len = strcspn(client_entry->hostname, ".");
1014       newnick = silc_realloc(newnick, sizeof(*newnick) * (off + len));
1015       memcpy(&newnick[off], client_entry->hostname, len);
1016       off += len;
1017       break;
1018     case 'H':
1019       /* Full hostname */
1020       if (!client_entry->hostname)
1021         break;
1022       len = strlen(client_entry->hostname);
1023       newnick = silc_realloc(newnick, sizeof(*newnick) * (off + len));
1024       memcpy(&newnick[off], client_entry->hostname, len);
1025       off += len;
1026       break;
1027     case 's':
1028       /* Stripped server name */
1029       if (!client_entry->server)
1030         break;
1031       len = strcspn(client_entry->server, ".");
1032       newnick = silc_realloc(newnick, sizeof(*newnick) * (off + len));
1033       memcpy(&newnick[off], client_entry->server, len);
1034       off += len;
1035       break;
1036     case 'S':
1037       /* Full server name */
1038       if (!client_entry->server)
1039         break;
1040       len = strlen(client_entry->server);
1041       newnick = silc_realloc(newnick, sizeof(*newnick) * (off + len));
1042       memcpy(&newnick[off], client_entry->server, len);
1043       off += len;
1044       break;
1045     case 'a':
1046       /* Ascending number */
1047       {
1048         char tmp[6];
1049         int num, max = 1;
1050
1051         if (clients_count == 1) {
1052           unformatted = clients[0];
1053           break;
1054         }
1055
1056         for (i = 0; i < clients_count; i++) {
1057           if (!strncasecmp(clients[i]->nickname, client_entry->nickname,
1058                            strlen(clients[i]->nickname)))
1059             unformatted = clients[i];
1060           if (strncasecmp(clients[i]->nickname, newnick, off))
1061             continue;
1062           if (strlen(clients[i]->nickname) <= off)
1063             continue;
1064           num = atoi(&clients[i]->nickname[off]);
1065           if (num > max)
1066             max = num;
1067         }
1068
1069         memset(tmp, 0, sizeof(tmp));
1070         snprintf(tmp, sizeof(tmp) - 1, "%d", ++max);
1071         len = strlen(tmp);
1072         newnick = silc_realloc(newnick, sizeof(*newnick) * (off + len));
1073         memcpy(&newnick[off], tmp, len);
1074         off += len;
1075       }
1076       break;
1077     default:
1078       /* Some other character in the string */
1079       newnick = silc_realloc(newnick, sizeof(*newnick) * (off + 1));
1080       memcpy(&newnick[off], cp, 1);
1081       off++;
1082       break;
1083     }
1084
1085     cp++;
1086   }
1087
1088   newnick = silc_realloc(newnick, sizeof(*newnick) * (off + 1));
1089   newnick[off] = 0;
1090
1091   /* If we are changing nickname of our local entry we'll enforce
1092      that we will always get the unformatted nickname.  Give our
1093      format number to the one that is not formatted now. */
1094   if (unformatted && client_entry == conn->local_entry)
1095     client_entry = unformatted;
1096
1097   silc_free(client_entry->nickname);
1098   client_entry->nickname = newnick;
1099   silc_free(clients);
1100 }