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   silc_parse_userfqdn(nickname, &nick, &client_entry->server);
547   silc_parse_userfqdn(username, &client_entry->username, 
548                       &client_entry->hostname);
549   if (userinfo)
550     client_entry->realname = strdup(userinfo);
551   client_entry->mode = mode;
552   if (nick)
553     client_entry->nickname = strdup(nick);
554
555   /* Format the nickname */
556   silc_client_nickname_format(client, conn, client_entry);
557   
558   /* Add client to cache, the non-formatted nickname is saved to cache */
559   if (!silc_idcache_add(conn->client_cache, nick, client_entry->id, 
560                         (void *)client_entry, FALSE)) {
561     silc_free(client_entry->nickname);
562     silc_free(client_entry->username);
563     silc_free(client_entry->hostname);
564     silc_free(client_entry->server);
565     silc_free(client_entry);
566     return NULL;
567   }
568
569   return client_entry;
570 }
571
572 /* Updates the `client_entry' with the new information sent as argument. */
573
574 void silc_client_update_client(SilcClient client,
575                                SilcClientConnection conn,
576                                SilcClientEntry client_entry,
577                                const char *nickname,
578                                const char *username,
579                                const char *userinfo,
580                                uint32 mode)
581 {
582   char *nick = NULL;
583
584   if (!client_entry->username && username)
585     silc_parse_userfqdn(username, &client_entry->username, 
586                         &client_entry->hostname);
587   if (!client_entry->realname && userinfo)
588     client_entry->realname = strdup(userinfo);
589   if (!client_entry->nickname && nickname) {
590     silc_parse_userfqdn(nickname, &nick, &client_entry->server);
591     client_entry->nickname = strdup(nick);
592     silc_client_nickname_format(client, conn, client_entry);
593   }
594   client_entry->mode = mode;
595
596   if (nick) {
597     /* Remove the old cache entry and create a new one */
598     silc_idcache_del_by_context(conn->client_cache, client_entry);
599     silc_idcache_add(conn->client_cache, nick, client_entry->id, 
600                      client_entry, FALSE);
601   }
602 }
603
604 /* Deletes the client entry and frees all memory. */
605
606 void silc_client_del_client_entry(SilcClient client, 
607                                   SilcClientEntry client_entry)
608 {
609   silc_free(client_entry->nickname);
610   silc_free(client_entry->username);
611   silc_free(client_entry->realname);
612   silc_free(client_entry->server);
613   silc_free(client_entry->id);
614   if (client_entry->send_key)
615     silc_cipher_free(client_entry->send_key);
616   if (client_entry->receive_key)
617     silc_cipher_free(client_entry->receive_key);
618   silc_free(client_entry->key);
619   silc_free(client_entry);
620 }
621
622 /* Removes client from the cache by the client entry. */
623
624 bool silc_client_del_client(SilcClient client, SilcClientConnection conn,
625                             SilcClientEntry client_entry)
626 {
627   bool ret = silc_idcache_del_by_context(conn->client_cache, client_entry);
628   silc_client_del_client_entry(client, client_entry);
629   return ret;
630 }
631
632 /* Removes channel from the cache by the channel entry. */
633
634 bool silc_client_del_channel(SilcClient client, SilcClientConnection conn,
635                              SilcChannelEntry channel)
636 {
637   bool ret = silc_idcache_del_by_context(conn->channel_cache, channel);
638   silc_free(channel->channel_name);
639   silc_free(channel->id);
640   silc_free(channel->key);
641   if (channel->channel_key)
642     silc_cipher_free(channel->channel_key);
643   if (channel->hmac)
644     silc_hmac_free(channel->hmac);
645   if (channel->old_channel_key)
646     silc_cipher_free(channel->old_channel_key);
647   if (channel->old_hmac)
648     silc_hmac_free(channel->old_hmac);
649   if (channel->rekey_task)
650     silc_schedule_task_del(conn->client->schedule, channel->rekey_task);
651   silc_client_del_channel_private_keys(client, conn, channel);
652   silc_free(channel);
653   return ret;
654 }
655
656 /* Finds entry for channel by the channel name. Returns the entry or NULL
657    if the entry was not found. It is found only if the client is joined
658    to the channel. */
659
660 SilcChannelEntry silc_client_get_channel(SilcClient client,
661                                          SilcClientConnection conn,
662                                          char *channel)
663 {
664   SilcIDCacheEntry id_cache;
665   SilcChannelEntry entry;
666
667   if (!silc_idcache_find_by_name_one(conn->channel_cache, channel, 
668                                      &id_cache))
669     return NULL;
670
671   entry = (SilcChannelEntry)id_cache->context;
672
673   return entry;
674 }
675
676 /* Finds entry for channel by the channel ID. Returns the entry or NULL
677    if the entry was not found. It is found only if the client is joined
678    to the channel. */
679
680 SilcChannelEntry silc_client_get_channel_by_id(SilcClient client,
681                                                SilcClientConnection conn,
682                                                SilcChannelID *channel_id)
683 {
684   SilcIDCacheEntry id_cache;
685   SilcChannelEntry entry;
686
687   if (!silc_idcache_find_by_id_one(conn->channel_cache, channel_id, 
688                                    &id_cache))
689     return NULL;
690
691   entry = (SilcChannelEntry)id_cache->context;
692
693   return entry;
694 }
695
696 typedef struct {
697   SilcClient client;
698   SilcClientConnection conn;
699   SilcChannelID *channel_id;
700   SilcGetChannelCallback completion;
701   void *context;
702   int found;
703 } *GetChannelByIDInternal;
704
705 SILC_CLIENT_CMD_FUNC(get_channel_by_id_callback)
706 {
707   GetChannelByIDInternal i = (GetChannelByIDInternal)context;
708   SilcChannelEntry entry;
709
710   /* Get the channel */
711   entry = silc_client_get_channel_by_id(i->client, i->conn,
712                                         i->channel_id);
713   if (entry) {
714     i->completion(i->client, i->conn, &entry, 1, i->context);
715     i->found = TRUE;
716   }
717 }
718
719 static void silc_client_get_channel_by_id_destructor(void *context)
720 {
721   GetChannelByIDInternal i = (GetChannelByIDInternal)context;
722
723   if (i->found == FALSE)
724     i->completion(i->client, i->conn, NULL, 0, i->context);
725
726   silc_free(i->channel_id);
727   silc_free(i);
728 }
729
730 /* Resolves channel information from the server by the channel ID. */
731
732 void silc_client_get_channel_by_id_resolve(SilcClient client,
733                                            SilcClientConnection conn,
734                                            SilcChannelID *channel_id,
735                                            SilcGetChannelCallback completion,
736                                            void *context)
737 {
738   SilcBuffer idp;
739   GetChannelByIDInternal i = silc_calloc(1, sizeof(*i));
740
741   idp = silc_id_payload_encode(channel_id, SILC_ID_CHANNEL);
742   silc_client_send_command(client, conn, SILC_COMMAND_IDENTIFY, 
743                            ++conn->cmd_ident,
744                            1, 5, idp->data, idp->len);
745   silc_buffer_free(idp);
746
747   i->client = client;
748   i->conn = conn;
749   i->channel_id = silc_id_dup(channel_id, SILC_ID_CHANNEL);
750   i->completion = completion;
751   i->context = context;
752       
753   /* Add pending callback */
754   silc_client_command_pending(conn, SILC_COMMAND_IDENTIFY, 
755                               conn->cmd_ident, 
756                               silc_client_get_channel_by_id_destructor,
757                               silc_client_command_get_channel_by_id_callback, 
758                               (void *)i);
759 }
760
761 /* Find channel entry by ID. This routine is used internally by the library. */
762
763 SilcChannelEntry silc_idlist_get_channel_by_id(SilcClient client,
764                                                SilcClientConnection conn,
765                                                SilcChannelID *channel_id,
766                                                int query)
767 {
768   SilcBuffer idp;
769   SilcChannelEntry channel;
770
771   channel = silc_client_get_channel_by_id(client, conn, channel_id);
772   if (channel)
773     return channel;
774
775   if (query) {
776     idp = silc_id_payload_encode(channel_id, SILC_ID_CHANNEL);
777     silc_client_send_command(client, conn, SILC_COMMAND_IDENTIFY, 
778                              ++conn->cmd_ident,
779                              1, 5, idp->data, idp->len);
780     silc_buffer_free(idp);
781   }
782
783   return NULL;
784 }
785
786 /* Finds entry for server by the server name. */
787
788 SilcServerEntry silc_client_get_server(SilcClient client,
789                                        SilcClientConnection conn,
790                                        char *server_name)
791 {
792   SilcIDCacheEntry id_cache;
793   SilcServerEntry entry;
794
795   if (!silc_idcache_find_by_name_one(conn->server_cache, server_name, 
796                                      &id_cache))
797     return NULL;
798
799   entry = (SilcServerEntry)id_cache->context;
800
801   return entry;
802 }
803
804 /* Finds entry for server by the server ID. */
805
806 SilcServerEntry silc_client_get_server_by_id(SilcClient client,
807                                              SilcClientConnection conn,
808                                              SilcServerID *server_id)
809 {
810   SilcIDCacheEntry id_cache;
811   SilcServerEntry entry;
812
813   if (!silc_idcache_find_by_id_one(conn->server_cache, (void *)server_id, 
814                                    &id_cache))
815     return NULL;
816
817   entry = (SilcServerEntry)id_cache->context;
818
819   return entry;
820 }
821
822 /* Removes server from the cache by the server entry. */
823
824 bool silc_client_del_server(SilcClient client, SilcClientConnection conn,
825                             SilcServerEntry server)
826 {
827   bool ret = silc_idcache_del_by_context(conn->server_cache, server);
828   silc_free(server->server_name);
829   silc_free(server->server_info);
830   silc_free(server->server_id);
831   silc_free(server);
832   return ret;
833 }
834
835 /* Formats the nickname of the client specified by the `client_entry'.
836    If the format is specified by the application this will format the
837    nickname and replace the old nickname in the client entry. If the
838    format string is not specified then this function has no effect. */
839
840 void silc_client_nickname_format(SilcClient client, 
841                                  SilcClientConnection conn,
842                                  SilcClientEntry client_entry)
843 {
844   char *cp;
845   char *newnick = NULL;
846   int i, off = 0, len;
847   SilcClientEntry *clients;
848   uint32 clients_count;
849
850   if (!client->params->nickname_format[0])
851     return;
852
853   if (!client_entry->nickname)
854     return;
855
856   /* Get all clients with same nickname. Do not perform the formatting
857      if there aren't any clients with same nickname unless the application
858      is forcing us to do so. */
859   clients = silc_client_get_clients_local(client, conn,
860                                           client_entry->nickname, NULL,
861                                           &clients_count);
862   if (!clients && !client->params->nickname_force_format)
863     return;
864
865   cp = client->params->nickname_format;
866   while (*cp) {
867     if (*cp == '%') {
868       cp++;
869       continue;
870     }
871
872     switch(*cp) {
873     case 'n':
874       /* Nickname */
875       if (!client_entry->nickname)
876         break;
877       len = strlen(client_entry->nickname);
878       newnick = silc_realloc(newnick, sizeof(*newnick) * (off + len));
879       memcpy(&newnick[off], client_entry->nickname, len);
880       off += len;
881       break;
882     case 'h':
883       /* Stripped hostname */
884       if (!client_entry->hostname)
885         break;
886       len = strcspn(client_entry->hostname, ".");
887       newnick = silc_realloc(newnick, sizeof(*newnick) * (off + len));
888       memcpy(&newnick[off], client_entry->hostname, len);
889       off += len;
890       break;
891     case 'H':
892       /* Full hostname */
893       if (!client_entry->hostname)
894         break;
895       len = strlen(client_entry->hostname);
896       newnick = silc_realloc(newnick, sizeof(*newnick) * (off + len));
897       memcpy(&newnick[off], client_entry->hostname, len);
898       off += len;
899       break;
900     case 's':
901       /* Stripped server name */
902       if (!client_entry->server)
903         break;
904       len = strcspn(client_entry->server, ".");
905       newnick = silc_realloc(newnick, sizeof(*newnick) * (off + len));
906       memcpy(&newnick[off], client_entry->server, len);
907       off += len;
908       break;
909     case 'S':
910       /* Full server name */
911       if (!client_entry->server)
912         break;
913       len = strlen(client_entry->server);
914       newnick = silc_realloc(newnick, sizeof(*newnick) * (off + len));
915       memcpy(&newnick[off], client_entry->server, len);
916       off += len;
917       break;
918     case 'a':
919       /* Ascending number */
920       {
921         char tmp[6];
922         int num, max = 1;
923
924         if (clients_count == 1)
925           break;
926
927         for (i = 0; i < clients_count; i++) {
928           if (strncmp(clients[i]->nickname, newnick, off))
929             continue;
930           if (strlen(clients[i]->nickname) <= off)
931             continue;
932           num = atoi(&clients[i]->nickname[off]);
933           if (num > max)
934             max = num;
935         }
936         
937         memset(tmp, 0, sizeof(tmp));
938         snprintf(tmp, sizeof(tmp) - 1, "%d", ++max);
939         len = strlen(tmp);
940         newnick = silc_realloc(newnick, sizeof(*newnick) * (off + len));
941         memcpy(&newnick[off], tmp, len);
942         off += len;
943       }
944       break;
945     default:
946       /* Some other character in the string */
947       newnick = silc_realloc(newnick, sizeof(*newnick) * (off + 1));
948       memcpy(&newnick[off], cp, 1);
949       off++;
950       break;
951     }
952
953     cp++;
954   }
955
956   newnick = silc_realloc(newnick, sizeof(*newnick) * (off + 1));
957   newnick[off] = 0;
958
959   silc_free(client_entry->nickname);
960   client_entry->nickname = newnick;
961   silc_free(clients);
962 }