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