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   SILC_LOG_DEBUG(("Start"));
726
727   /* Remove the context from the client's channel hash table as that
728      table and channel's user_list hash table share this same context. */
729   silc_hash_table_del(chu->client->channels, chu->channel);
730   silc_free(chu);
731 }
732
733 /* Removes channel from the cache by the channel entry. */
734
735 bool silc_client_del_channel(SilcClient client, SilcClientConnection conn,
736                              SilcChannelEntry channel)
737 {
738   bool ret = silc_idcache_del_by_context(conn->channel_cache, channel);
739
740   SILC_LOG_DEBUG(("Start"));
741
742   /* Free all client entrys from the users list. The silc_hash_table_free
743      will free all the entries so they are not freed at the foreach 
744      callback. */
745   silc_hash_table_foreach(channel->user_list, silc_client_del_channel_foreach,
746                           NULL);
747   silc_hash_table_free(channel->user_list);
748
749   silc_free(channel->channel_name);
750   silc_free(channel->id);
751   silc_free(channel->key);
752   if (channel->channel_key)
753     silc_cipher_free(channel->channel_key);
754   if (channel->hmac)
755     silc_hmac_free(channel->hmac);
756   if (channel->old_channel_key)
757     silc_cipher_free(channel->old_channel_key);
758   if (channel->old_hmac)
759     silc_hmac_free(channel->old_hmac);
760   if (channel->rekey_task)
761     silc_schedule_task_del(conn->client->schedule, channel->rekey_task);
762   silc_client_del_channel_private_keys(client, conn, channel);
763   silc_free(channel);
764   return ret;
765 }
766
767 /* Replaces the channel ID of the `channel' to `new_id'. Returns FALSE
768    if the ID could not be changed. */
769
770 bool silc_client_replace_channel_id(SilcClient client,
771                                     SilcClientConnection conn,
772                                     SilcChannelEntry channel,
773                                     SilcChannelID *new_id)
774 {
775   if (!new_id)
776     return FALSE;
777
778   SILC_LOG_DEBUG(("Old Channel ID id(%s)", 
779                   silc_id_render(channel->id, SILC_ID_CHANNEL)));
780   SILC_LOG_DEBUG(("New Channel ID id(%s)", 
781                   silc_id_render(new_id, SILC_ID_CHANNEL)));
782
783   silc_idcache_del_by_id(conn->channel_cache, channel->id);
784   silc_free(channel->id);
785   channel->id = new_id;
786   return silc_idcache_add(conn->channel_cache, channel->channel_name, 
787                           (void *)channel->id, (void *)channel, 0, NULL);
788
789 }
790
791 /* Finds entry for channel by the channel name. Returns the entry or NULL
792    if the entry was not found. It is found only if the client is joined
793    to the channel. */
794
795 SilcChannelEntry silc_client_get_channel(SilcClient client,
796                                          SilcClientConnection conn,
797                                          char *channel)
798 {
799   SilcIDCacheEntry id_cache;
800   SilcChannelEntry entry;
801
802   SILC_LOG_DEBUG(("Start"));
803
804   if (!silc_idcache_find_by_name_one(conn->channel_cache, channel, 
805                                      &id_cache))
806     return NULL;
807
808   entry = (SilcChannelEntry)id_cache->context;
809
810   SILC_LOG_DEBUG(("Found"));
811
812   return entry;
813 }
814
815 /* Finds entry for channel by the channel ID. Returns the entry or NULL
816    if the entry was not found. It is found only if the client is joined
817    to the channel. */
818
819 SilcChannelEntry silc_client_get_channel_by_id(SilcClient client,
820                                                SilcClientConnection conn,
821                                                SilcChannelID *channel_id)
822 {
823   SilcIDCacheEntry id_cache;
824   SilcChannelEntry entry;
825
826   SILC_LOG_DEBUG(("Start"));
827
828   if (!silc_idcache_find_by_id_one(conn->channel_cache, channel_id, 
829                                    &id_cache))
830     return NULL;
831
832   entry = (SilcChannelEntry)id_cache->context;
833
834   SILC_LOG_DEBUG(("Found"));
835
836   return entry;
837 }
838
839 typedef struct {
840   SilcClient client;
841   SilcClientConnection conn;
842   SilcChannelID *channel_id;
843   SilcGetChannelCallback completion;
844   void *context;
845 } *GetChannelByIDInternal;
846
847 SILC_CLIENT_CMD_FUNC(get_channel_by_id_callback)
848 {
849   GetChannelByIDInternal i = (GetChannelByIDInternal)context;
850   SilcChannelEntry entry;
851
852   SILC_LOG_DEBUG(("Start"));
853
854   /* Get the channel */
855   entry = silc_client_get_channel_by_id(i->client, i->conn, i->channel_id);
856   if (entry) {
857     i->completion(i->client, i->conn, &entry, 1, i->context);
858   } else {
859     i->completion(i->client, i->conn, NULL, 0, i->context);
860   }
861
862   silc_free(i->channel_id);
863   silc_free(i);
864 }
865
866 /* Resolves channel information from the server by the channel ID. */
867
868 void silc_client_get_channel_by_id_resolve(SilcClient client,
869                                            SilcClientConnection conn,
870                                            SilcChannelID *channel_id,
871                                            SilcGetChannelCallback completion,
872                                            void *context)
873 {
874   SilcBuffer idp;
875   GetChannelByIDInternal i = silc_calloc(1, sizeof(*i));
876
877   SILC_LOG_DEBUG(("Start"));
878
879   i->client = client;
880   i->conn = conn;
881   i->channel_id = silc_id_dup(channel_id, SILC_ID_CHANNEL);
882   i->completion = completion;
883   i->context = context;
884       
885   /* Register our own command reply for this command */
886   silc_client_command_register(client, SILC_COMMAND_IDENTIFY, NULL, NULL,
887                                silc_client_command_reply_identify_i, 0,
888                                ++conn->cmd_ident);
889
890   /* Send the command */
891   idp = silc_id_payload_encode(channel_id, SILC_ID_CHANNEL);
892   silc_client_command_send(client, conn, SILC_COMMAND_IDENTIFY, 
893                            conn->cmd_ident,
894                            1, 5, idp->data, idp->len);
895   silc_buffer_free(idp);
896
897   /* Add pending callback */
898   silc_client_command_pending(conn, SILC_COMMAND_IDENTIFY, conn->cmd_ident,
899                               silc_client_command_get_channel_by_id_callback, 
900                               (void *)i);
901 }
902
903 /* Finds entry for server by the server name. */
904
905 SilcServerEntry silc_client_get_server(SilcClient client,
906                                        SilcClientConnection conn,
907                                        char *server_name)
908 {
909   SilcIDCacheEntry id_cache;
910   SilcServerEntry entry;
911
912   SILC_LOG_DEBUG(("Start"));
913
914   if (!silc_idcache_find_by_name_one(conn->server_cache, server_name, 
915                                      &id_cache))
916     return NULL;
917
918   entry = (SilcServerEntry)id_cache->context;
919
920   return entry;
921 }
922
923 /* Finds entry for server by the server ID. */
924
925 SilcServerEntry silc_client_get_server_by_id(SilcClient client,
926                                              SilcClientConnection conn,
927                                              SilcServerID *server_id)
928 {
929   SilcIDCacheEntry id_cache;
930   SilcServerEntry entry;
931
932   SILC_LOG_DEBUG(("Start"));
933
934   if (!silc_idcache_find_by_id_one(conn->server_cache, (void *)server_id, 
935                                    &id_cache))
936     return NULL;
937
938   entry = (SilcServerEntry)id_cache->context;
939
940   return entry;
941 }
942
943 /* Removes server from the cache by the server entry. */
944
945 bool silc_client_del_server(SilcClient client, SilcClientConnection conn,
946                             SilcServerEntry server)
947 {
948   bool ret = silc_idcache_del_by_context(conn->server_cache, server);
949   silc_free(server->server_name);
950   silc_free(server->server_info);
951   silc_free(server->server_id);
952   silc_free(server);
953   return ret;
954 }
955
956 /* Formats the nickname of the client specified by the `client_entry'.
957    If the format is specified by the application this will format the
958    nickname and replace the old nickname in the client entry. If the
959    format string is not specified then this function has no effect. */
960
961 void silc_client_nickname_format(SilcClient client, 
962                                  SilcClientConnection conn,
963                                  SilcClientEntry client_entry)
964 {
965   char *cp;
966   char *newnick = NULL;
967   int i, off = 0, len;
968   SilcClientEntry *clients;
969   uint32 clients_count = 0;
970   SilcClientEntry unformatted = NULL;
971
972   SILC_LOG_DEBUG(("Start"));
973
974   if (!client->internal->params->nickname_format[0])
975     return;
976
977   if (!client_entry->nickname)
978     return;
979
980   /* Get all clients with same nickname. Do not perform the formatting
981      if there aren't any clients with same nickname unless the application
982      is forcing us to do so. */
983   clients = silc_client_get_clients_local(client, conn,
984                                           client_entry->nickname, NULL,
985                                           &clients_count);
986   if (!clients && !client->internal->params->nickname_force_format)
987     return;
988
989   len = 0;
990   for (i = 0; i < clients_count; i++)
991     if (clients[i]->valid && clients[i] != client_entry)
992       len++;
993   if (!len)
994     return;
995
996   cp = client->internal->params->nickname_format;
997   while (*cp) {
998     if (*cp == '%') {
999       cp++;
1000       continue;
1001     }
1002
1003     switch(*cp) {
1004     case 'n':
1005       /* Nickname */
1006       if (!client_entry->nickname)
1007         break;
1008       len = strlen(client_entry->nickname);
1009       newnick = silc_realloc(newnick, sizeof(*newnick) * (off + len));
1010       memcpy(&newnick[off], client_entry->nickname, len);
1011       off += len;
1012       break;
1013     case 'h':
1014       /* Stripped hostname */
1015       if (!client_entry->hostname)
1016         break;
1017       len = strcspn(client_entry->hostname, ".");
1018       newnick = silc_realloc(newnick, sizeof(*newnick) * (off + len));
1019       memcpy(&newnick[off], client_entry->hostname, len);
1020       off += len;
1021       break;
1022     case 'H':
1023       /* Full hostname */
1024       if (!client_entry->hostname)
1025         break;
1026       len = strlen(client_entry->hostname);
1027       newnick = silc_realloc(newnick, sizeof(*newnick) * (off + len));
1028       memcpy(&newnick[off], client_entry->hostname, len);
1029       off += len;
1030       break;
1031     case 's':
1032       /* Stripped server name */
1033       if (!client_entry->server)
1034         break;
1035       len = strcspn(client_entry->server, ".");
1036       newnick = silc_realloc(newnick, sizeof(*newnick) * (off + len));
1037       memcpy(&newnick[off], client_entry->server, len);
1038       off += len;
1039       break;
1040     case 'S':
1041       /* Full server name */
1042       if (!client_entry->server)
1043         break;
1044       len = strlen(client_entry->server);
1045       newnick = silc_realloc(newnick, sizeof(*newnick) * (off + len));
1046       memcpy(&newnick[off], client_entry->server, len);
1047       off += len;
1048       break;
1049     case 'a':
1050       /* Ascending number */
1051       {
1052         char tmp[6];
1053         int num, max = 1;
1054
1055         if (clients_count == 1) {
1056           unformatted = clients[0];
1057           break;
1058         }
1059
1060         for (i = 0; i < clients_count; i++) {
1061           if (!strncasecmp(clients[i]->nickname, client_entry->nickname,
1062                            strlen(clients[i]->nickname)))
1063             unformatted = clients[i];
1064           if (strncasecmp(clients[i]->nickname, newnick, off))
1065             continue;
1066           if (strlen(clients[i]->nickname) <= off)
1067             continue;
1068           num = atoi(&clients[i]->nickname[off]);
1069           if (num > max)
1070             max = num;
1071         }
1072
1073         memset(tmp, 0, sizeof(tmp));
1074         snprintf(tmp, sizeof(tmp) - 1, "%d", ++max);
1075         len = strlen(tmp);
1076         newnick = silc_realloc(newnick, sizeof(*newnick) * (off + len));
1077         memcpy(&newnick[off], tmp, len);
1078         off += len;
1079       }
1080       break;
1081     default:
1082       /* Some other character in the string */
1083       newnick = silc_realloc(newnick, sizeof(*newnick) * (off + 1));
1084       memcpy(&newnick[off], cp, 1);
1085       off++;
1086       break;
1087     }
1088
1089     cp++;
1090   }
1091
1092   newnick = silc_realloc(newnick, sizeof(*newnick) * (off + 1));
1093   newnick[off] = 0;
1094
1095   /* If we are changing nickname of our local entry we'll enforce
1096      that we will always get the unformatted nickname.  Give our
1097      format number to the one that is not formatted now. */
1098   if (unformatted && client_entry == conn->local_entry)
1099     client_entry = unformatted;
1100
1101   silc_free(client_entry->nickname);
1102   client_entry->nickname = newnick;
1103   silc_free(clients);
1104 }