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