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