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