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