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   silc_client_del_channel_private_keys(client, conn, channel);
646   silc_free(channel);
647   return ret;
648 }
649
650 /* Finds entry for channel by the channel name. Returns the entry or NULL
651    if the entry was not found. It is found only if the client is joined
652    to the channel. */
653
654 SilcChannelEntry silc_client_get_channel(SilcClient client,
655                                          SilcClientConnection conn,
656                                          char *channel)
657 {
658   SilcIDCacheEntry id_cache;
659   SilcChannelEntry entry;
660
661   if (!silc_idcache_find_by_name_one(conn->channel_cache, channel, 
662                                      &id_cache))
663     return NULL;
664
665   entry = (SilcChannelEntry)id_cache->context;
666
667   return entry;
668 }
669
670 /* Finds entry for channel by the channel ID. Returns the entry or NULL
671    if the entry was not found. It is found only if the client is joined
672    to the channel. */
673
674 SilcChannelEntry silc_client_get_channel_by_id(SilcClient client,
675                                                SilcClientConnection conn,
676                                                SilcChannelID *channel_id)
677 {
678   SilcIDCacheEntry id_cache;
679   SilcChannelEntry entry;
680
681   if (!silc_idcache_find_by_id_one(conn->channel_cache, channel_id, 
682                                    &id_cache))
683     return NULL;
684
685   entry = (SilcChannelEntry)id_cache->context;
686
687   return entry;
688 }
689
690 typedef struct {
691   SilcClient client;
692   SilcClientConnection conn;
693   SilcChannelID *channel_id;
694   SilcGetChannelCallback completion;
695   void *context;
696   int found;
697 } *GetChannelByIDInternal;
698
699 SILC_CLIENT_CMD_FUNC(get_channel_by_id_callback)
700 {
701   GetChannelByIDInternal i = (GetChannelByIDInternal)context;
702   SilcChannelEntry entry;
703
704   /* Get the channel */
705   entry = silc_client_get_channel_by_id(i->client, i->conn,
706                                         i->channel_id);
707   if (entry) {
708     i->completion(i->client, i->conn, &entry, 1, i->context);
709     i->found = TRUE;
710   }
711 }
712
713 static void silc_client_get_channel_by_id_destructor(void *context)
714 {
715   GetChannelByIDInternal i = (GetChannelByIDInternal)context;
716
717   if (i->found == FALSE)
718     i->completion(i->client, i->conn, NULL, 0, i->context);
719
720   silc_free(i->channel_id);
721   silc_free(i);
722 }
723
724 /* Resolves channel information from the server by the channel ID. */
725
726 void silc_client_get_channel_by_id_resolve(SilcClient client,
727                                            SilcClientConnection conn,
728                                            SilcChannelID *channel_id,
729                                            SilcGetChannelCallback completion,
730                                            void *context)
731 {
732   SilcBuffer idp;
733   GetChannelByIDInternal i = silc_calloc(1, sizeof(*i));
734
735   idp = silc_id_payload_encode(channel_id, SILC_ID_CHANNEL);
736   silc_client_send_command(client, conn, SILC_COMMAND_IDENTIFY, 
737                            ++conn->cmd_ident,
738                            1, 5, idp->data, idp->len);
739   silc_buffer_free(idp);
740
741   i->client = client;
742   i->conn = conn;
743   i->channel_id = silc_id_dup(channel_id, SILC_ID_CHANNEL);
744   i->completion = completion;
745   i->context = context;
746       
747   /* Add pending callback */
748   silc_client_command_pending(conn, SILC_COMMAND_IDENTIFY, 
749                               conn->cmd_ident, 
750                               silc_client_get_channel_by_id_destructor,
751                               silc_client_command_get_channel_by_id_callback, 
752                               (void *)i);
753 }
754
755 /* Find channel entry by ID. This routine is used internally by the library. */
756
757 SilcChannelEntry silc_idlist_get_channel_by_id(SilcClient client,
758                                                SilcClientConnection conn,
759                                                SilcChannelID *channel_id,
760                                                int query)
761 {
762   SilcBuffer idp;
763   SilcChannelEntry channel;
764
765   channel = silc_client_get_channel_by_id(client, conn, channel_id);
766   if (channel)
767     return channel;
768
769   if (query) {
770     idp = silc_id_payload_encode(channel_id, SILC_ID_CHANNEL);
771     silc_client_send_command(client, conn, SILC_COMMAND_IDENTIFY, 
772                              ++conn->cmd_ident,
773                              1, 5, idp->data, idp->len);
774     silc_buffer_free(idp);
775   }
776
777   return NULL;
778 }
779
780 /* Finds entry for server by the server name. */
781
782 SilcServerEntry silc_client_get_server(SilcClient client,
783                                        SilcClientConnection conn,
784                                        char *server_name)
785 {
786   SilcIDCacheEntry id_cache;
787   SilcServerEntry entry;
788
789   if (!silc_idcache_find_by_name_one(conn->server_cache, server_name, 
790                                      &id_cache))
791     return NULL;
792
793   entry = (SilcServerEntry)id_cache->context;
794
795   return entry;
796 }
797
798 /* Finds entry for server by the server ID. */
799
800 SilcServerEntry silc_client_get_server_by_id(SilcClient client,
801                                              SilcClientConnection conn,
802                                              SilcServerID *server_id)
803 {
804   SilcIDCacheEntry id_cache;
805   SilcServerEntry entry;
806
807   if (!silc_idcache_find_by_id_one(conn->server_cache, (void *)server_id, 
808                                    &id_cache))
809     return NULL;
810
811   entry = (SilcServerEntry)id_cache->context;
812
813   return entry;
814 }
815
816 /* Removes server from the cache by the server entry. */
817
818 bool silc_client_del_server(SilcClient client, SilcClientConnection conn,
819                             SilcServerEntry server)
820 {
821   bool ret = silc_idcache_del_by_context(conn->server_cache, server);
822   silc_free(server->server_name);
823   silc_free(server->server_info);
824   silc_free(server->server_id);
825   silc_free(server);
826   return ret;
827 }
828
829 /* Formats the nickname of the client specified by the `client_entry'.
830    If the format is specified by the application this will format the
831    nickname and replace the old nickname in the client entry. If the
832    format string is not specified then this function has no effect. */
833
834 void silc_client_nickname_format(SilcClient client, 
835                                  SilcClientConnection conn,
836                                  SilcClientEntry client_entry)
837 {
838   char *cp;
839   char *newnick = NULL;
840   int i, off = 0, len;
841   SilcClientEntry *clients;
842   uint32 clients_count;
843
844   if (!client->params->nickname_format[0])
845     return;
846
847   if (!client_entry->nickname)
848     return;
849
850   /* Get all clients with same nickname. Do not perform the formatting
851      if there aren't any clients with same nickname unless the application
852      is forcing us to do so. */
853   clients = silc_client_get_clients_local(client, conn,
854                                           client_entry->nickname, NULL,
855                                           &clients_count);
856   if (!clients && !client->params->nickname_force_format)
857     return;
858
859   cp = client->params->nickname_format;
860   while (*cp) {
861     if (*cp == '%') {
862       cp++;
863       continue;
864     }
865
866     switch(*cp) {
867     case 'n':
868       /* Nickname */
869       if (!client_entry->nickname)
870         break;
871       len = strlen(client_entry->nickname);
872       newnick = silc_realloc(newnick, sizeof(*newnick) * (off + len));
873       memcpy(&newnick[off], client_entry->nickname, len);
874       off += len;
875       break;
876     case 'h':
877       /* Stripped hostname */
878       if (!client_entry->hostname)
879         break;
880       len = strcspn(client_entry->hostname, ".");
881       newnick = silc_realloc(newnick, sizeof(*newnick) * (off + len));
882       memcpy(&newnick[off], client_entry->hostname, len);
883       off += len;
884       break;
885     case 'H':
886       /* Full hostname */
887       if (!client_entry->hostname)
888         break;
889       len = strlen(client_entry->hostname);
890       newnick = silc_realloc(newnick, sizeof(*newnick) * (off + len));
891       memcpy(&newnick[off], client_entry->hostname, len);
892       off += len;
893       break;
894     case 's':
895       /* Stripped server name */
896       if (!client_entry->server)
897         break;
898       len = strcspn(client_entry->server, ".");
899       newnick = silc_realloc(newnick, sizeof(*newnick) * (off + len));
900       memcpy(&newnick[off], client_entry->server, len);
901       off += len;
902       break;
903     case 'S':
904       /* Full server name */
905       if (!client_entry->server)
906         break;
907       len = strlen(client_entry->server);
908       newnick = silc_realloc(newnick, sizeof(*newnick) * (off + len));
909       memcpy(&newnick[off], client_entry->server, len);
910       off += len;
911       break;
912     case 'a':
913       /* Ascending number */
914       {
915         char tmp[6];
916         int num, max = 1;
917
918         if (clients_count == 1)
919           break;
920
921         for (i = 0; i < clients_count; i++) {
922           if (strncmp(clients[i]->nickname, newnick, off))
923             continue;
924           if (strlen(clients[i]->nickname) <= off)
925             continue;
926           num = atoi(&clients[i]->nickname[off]);
927           if (num > max)
928             max = num;
929         }
930         
931         memset(tmp, 0, sizeof(tmp));
932         snprintf(tmp, sizeof(tmp) - 1, "%d", ++max);
933         len = strlen(tmp);
934         newnick = silc_realloc(newnick, sizeof(*newnick) * (off + len));
935         memcpy(&newnick[off], tmp, len);
936         off += len;
937       }
938       break;
939     default:
940       /* Some other character in the string */
941       newnick = silc_realloc(newnick, sizeof(*newnick) * (off + 1));
942       memcpy(&newnick[off], cp, 1);
943       off++;
944       break;
945     }
946
947     cp++;
948   }
949
950   newnick = silc_realloc(newnick, sizeof(*newnick) * (off + 1));
951   newnick[off] = 0;
952
953   silc_free(client_entry->nickname);
954   client_entry->nickname = newnick;
955   silc_free(clients);
956 }