updates.
[silc.git] / lib / silcclient / idlist.c
1 /*
2
3   idlist.c
4
5   Author: Pekka Riikonen <priikone@silcnet.org>
6
7   Copyright (C) 2001 - 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   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
704   /* Remove from channels */
705   silc_client_remove_from_channels(client, conn, client_entry);
706
707   /* Free the client entry data */
708   silc_client_del_client_entry(client, conn, client_entry);
709
710   return ret;
711 }
712
713 /* Removes channel from the cache by the channel entry. */
714
715 bool silc_client_del_channel(SilcClient client, SilcClientConnection conn,
716                              SilcChannelEntry channel)
717 {
718   bool ret = silc_idcache_del_by_context(conn->channel_cache, channel);
719   silc_free(channel->channel_name);
720   silc_free(channel->id);
721   silc_free(channel->key);
722   if (channel->channel_key)
723     silc_cipher_free(channel->channel_key);
724   if (channel->hmac)
725     silc_hmac_free(channel->hmac);
726   if (channel->old_channel_key)
727     silc_cipher_free(channel->old_channel_key);
728   if (channel->old_hmac)
729     silc_hmac_free(channel->old_hmac);
730   if (channel->rekey_task)
731     silc_schedule_task_del(conn->client->schedule, channel->rekey_task);
732   silc_client_del_channel_private_keys(client, conn, channel);
733   silc_free(channel);
734   return ret;
735 }
736
737 /* Finds entry for channel by the channel name. Returns the entry or NULL
738    if the entry was not found. It is found only if the client is joined
739    to the channel. */
740
741 SilcChannelEntry silc_client_get_channel(SilcClient client,
742                                          SilcClientConnection conn,
743                                          char *channel)
744 {
745   SilcIDCacheEntry id_cache;
746   SilcChannelEntry entry;
747
748   SILC_LOG_DEBUG(("Start"));
749
750   if (!silc_idcache_find_by_name_one(conn->channel_cache, channel, 
751                                      &id_cache))
752     return NULL;
753
754   entry = (SilcChannelEntry)id_cache->context;
755
756   SILC_LOG_DEBUG(("Found"));
757
758   return entry;
759 }
760
761 /* Finds entry for channel by the channel ID. Returns the entry or NULL
762    if the entry was not found. It is found only if the client is joined
763    to the channel. */
764
765 SilcChannelEntry silc_client_get_channel_by_id(SilcClient client,
766                                                SilcClientConnection conn,
767                                                SilcChannelID *channel_id)
768 {
769   SilcIDCacheEntry id_cache;
770   SilcChannelEntry entry;
771
772   SILC_LOG_DEBUG(("Start"));
773
774   if (!silc_idcache_find_by_id_one(conn->channel_cache, channel_id, 
775                                    &id_cache))
776     return NULL;
777
778   entry = (SilcChannelEntry)id_cache->context;
779
780   SILC_LOG_DEBUG(("Found"));
781
782   return entry;
783 }
784
785 typedef struct {
786   SilcClient client;
787   SilcClientConnection conn;
788   SilcChannelID *channel_id;
789   SilcGetChannelCallback completion;
790   void *context;
791   int found;
792 } *GetChannelByIDInternal;
793
794 SILC_CLIENT_CMD_FUNC(get_channel_by_id_callback)
795 {
796   GetChannelByIDInternal i = (GetChannelByIDInternal)context;
797   SilcChannelEntry entry;
798
799   SILC_LOG_DEBUG(("Start"));
800
801   /* Get the channel */
802   entry = silc_client_get_channel_by_id(i->client, i->conn,
803                                         i->channel_id);
804   if (entry) {
805     i->completion(i->client, i->conn, &entry, 1, i->context);
806     i->found = TRUE;
807   }
808 }
809
810 static void silc_client_get_channel_by_id_destructor(void *context)
811 {
812   GetChannelByIDInternal i = (GetChannelByIDInternal)context;
813
814   if (i->found == FALSE)
815     i->completion(i->client, i->conn, NULL, 0, i->context);
816
817   silc_free(i->channel_id);
818   silc_free(i);
819 }
820
821 /* Resolves channel information from the server by the channel ID. */
822
823 void silc_client_get_channel_by_id_resolve(SilcClient client,
824                                            SilcClientConnection conn,
825                                            SilcChannelID *channel_id,
826                                            SilcGetChannelCallback completion,
827                                            void *context)
828 {
829   SilcBuffer idp;
830   GetChannelByIDInternal i = silc_calloc(1, sizeof(*i));
831
832   SILC_LOG_DEBUG(("Start"));
833
834   i->client = client;
835   i->conn = conn;
836   i->channel_id = silc_id_dup(channel_id, SILC_ID_CHANNEL);
837   i->completion = completion;
838   i->context = context;
839       
840   /* Register our own command reply for this command */
841   silc_client_command_register(client, SILC_COMMAND_IDENTIFY, NULL, NULL,
842                                silc_client_command_reply_identify_i, 0,
843                                ++conn->cmd_ident);
844
845   /* Send the command */
846   idp = silc_id_payload_encode(channel_id, SILC_ID_CHANNEL);
847   silc_client_command_send(client, conn, SILC_COMMAND_IDENTIFY, 
848                            conn->cmd_ident,
849                            1, 5, idp->data, idp->len);
850   silc_buffer_free(idp);
851
852   /* Add pending callback */
853   silc_client_command_pending(conn, SILC_COMMAND_IDENTIFY, conn->cmd_ident,
854                               silc_client_get_channel_by_id_destructor,
855                               silc_client_command_get_channel_by_id_callback, 
856                               (void *)i);
857 }
858
859 /* Find channel entry by ID. This routine is used internally by the library. */
860
861 SilcChannelEntry silc_idlist_get_channel_by_id(SilcClient client,
862                                                SilcClientConnection conn,
863                                                SilcChannelID *channel_id,
864                                                int query)
865 {
866   SilcBuffer idp;
867   SilcChannelEntry channel;
868
869   SILC_LOG_DEBUG(("Start"));
870
871   channel = silc_client_get_channel_by_id(client, conn, channel_id);
872   if (channel)
873     return channel;
874
875   if (query) {
876     /* Register our own command reply for this command */
877     silc_client_command_register(client, SILC_COMMAND_IDENTIFY, NULL, NULL,
878                                  silc_client_command_reply_identify_i, 0,
879                                  ++conn->cmd_ident);
880
881     /* Send the command */
882     idp = silc_id_payload_encode(channel_id, SILC_ID_CHANNEL);
883     silc_client_command_send(client, conn, SILC_COMMAND_IDENTIFY, 
884                              conn->cmd_ident,
885                              1, 5, idp->data, idp->len);
886     silc_buffer_free(idp);
887   }
888
889   return NULL;
890 }
891
892 /* Finds entry for server by the server name. */
893
894 SilcServerEntry silc_client_get_server(SilcClient client,
895                                        SilcClientConnection conn,
896                                        char *server_name)
897 {
898   SilcIDCacheEntry id_cache;
899   SilcServerEntry entry;
900
901   SILC_LOG_DEBUG(("Start"));
902
903   if (!silc_idcache_find_by_name_one(conn->server_cache, server_name, 
904                                      &id_cache))
905     return NULL;
906
907   entry = (SilcServerEntry)id_cache->context;
908
909   return entry;
910 }
911
912 /* Finds entry for server by the server ID. */
913
914 SilcServerEntry silc_client_get_server_by_id(SilcClient client,
915                                              SilcClientConnection conn,
916                                              SilcServerID *server_id)
917 {
918   SilcIDCacheEntry id_cache;
919   SilcServerEntry entry;
920
921   SILC_LOG_DEBUG(("Start"));
922
923   if (!silc_idcache_find_by_id_one(conn->server_cache, (void *)server_id, 
924                                    &id_cache))
925     return NULL;
926
927   entry = (SilcServerEntry)id_cache->context;
928
929   return entry;
930 }
931
932 /* Removes server from the cache by the server entry. */
933
934 bool silc_client_del_server(SilcClient client, SilcClientConnection conn,
935                             SilcServerEntry server)
936 {
937   bool ret = silc_idcache_del_by_context(conn->server_cache, server);
938   silc_free(server->server_name);
939   silc_free(server->server_info);
940   silc_free(server->server_id);
941   silc_free(server);
942   return ret;
943 }
944
945 /* Formats the nickname of the client specified by the `client_entry'.
946    If the format is specified by the application this will format the
947    nickname and replace the old nickname in the client entry. If the
948    format string is not specified then this function has no effect. */
949
950 void silc_client_nickname_format(SilcClient client, 
951                                  SilcClientConnection conn,
952                                  SilcClientEntry client_entry)
953 {
954   char *cp;
955   char *newnick = NULL;
956   int i, off = 0, len;
957   SilcClientEntry *clients;
958   uint32 clients_count = 0;
959
960   SILC_LOG_DEBUG(("Start"));
961
962   if (!client->internal->params->nickname_format[0])
963     return;
964
965   if (!client_entry->nickname)
966     return;
967
968   /* Get all clients with same nickname. Do not perform the formatting
969      if there aren't any clients with same nickname unless the application
970      is forcing us to do so. */
971   clients = silc_client_get_clients_local(client, conn,
972                                           client_entry->nickname, NULL,
973                                           &clients_count);
974   if (!clients && !client->internal->params->nickname_force_format)
975     return;
976
977   len = 0;
978   for (i = 0; i < clients_count; i++)
979     if (clients[i]->valid && clients[i] != client_entry)
980       len++;
981   if (!len)
982     return;
983
984   cp = client->internal->params->nickname_format;
985   while (*cp) {
986     if (*cp == '%') {
987       cp++;
988       continue;
989     }
990
991     switch(*cp) {
992     case 'n':
993       /* Nickname */
994       if (!client_entry->nickname)
995         break;
996       len = strlen(client_entry->nickname);
997       newnick = silc_realloc(newnick, sizeof(*newnick) * (off + len));
998       memcpy(&newnick[off], client_entry->nickname, len);
999       off += len;
1000       break;
1001     case 'h':
1002       /* Stripped hostname */
1003       if (!client_entry->hostname)
1004         break;
1005       len = strcspn(client_entry->hostname, ".");
1006       newnick = silc_realloc(newnick, sizeof(*newnick) * (off + len));
1007       memcpy(&newnick[off], client_entry->hostname, len);
1008       off += len;
1009       break;
1010     case 'H':
1011       /* Full hostname */
1012       if (!client_entry->hostname)
1013         break;
1014       len = strlen(client_entry->hostname);
1015       newnick = silc_realloc(newnick, sizeof(*newnick) * (off + len));
1016       memcpy(&newnick[off], client_entry->hostname, len);
1017       off += len;
1018       break;
1019     case 's':
1020       /* Stripped server name */
1021       if (!client_entry->server)
1022         break;
1023       len = strcspn(client_entry->server, ".");
1024       newnick = silc_realloc(newnick, sizeof(*newnick) * (off + len));
1025       memcpy(&newnick[off], client_entry->server, len);
1026       off += len;
1027       break;
1028     case 'S':
1029       /* Full server name */
1030       if (!client_entry->server)
1031         break;
1032       len = strlen(client_entry->server);
1033       newnick = silc_realloc(newnick, sizeof(*newnick) * (off + len));
1034       memcpy(&newnick[off], client_entry->server, len);
1035       off += len;
1036       break;
1037     case 'a':
1038       /* Ascending number */
1039       {
1040         char tmp[6];
1041         int num, max = 1;
1042
1043         if (clients_count == 1)
1044           break;
1045
1046         for (i = 0; i < clients_count; i++) {
1047           if (strncasecmp(clients[i]->nickname, newnick, off))
1048             continue;
1049           if (strlen(clients[i]->nickname) <= off)
1050             continue;
1051           num = atoi(&clients[i]->nickname[off]);
1052           if (num > max)
1053             max = num;
1054         }
1055         
1056         memset(tmp, 0, sizeof(tmp));
1057         snprintf(tmp, sizeof(tmp) - 1, "%d", ++max);
1058         len = strlen(tmp);
1059         newnick = silc_realloc(newnick, sizeof(*newnick) * (off + len));
1060         memcpy(&newnick[off], tmp, len);
1061         off += len;
1062       }
1063       break;
1064     default:
1065       /* Some other character in the string */
1066       newnick = silc_realloc(newnick, sizeof(*newnick) * (off + 1));
1067       memcpy(&newnick[off], cp, 1);
1068       off++;
1069       break;
1070     }
1071
1072     cp++;
1073   }
1074
1075   newnick = silc_realloc(newnick, sizeof(*newnick) * (off + 1));
1076   newnick[off] = 0;
1077
1078   silc_free(client_entry->nickname);
1079   client_entry->nickname = newnick;
1080   silc_free(clients);
1081 }