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