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