0dd94e4ae9cc364d5c23bc20c4ad92733f047119
[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->hostname);
653   silc_free(client_entry->server);
654   silc_free(client_entry->id);
655   silc_free(client_entry->fingerprint);
656   if (client_entry->send_key)
657     silc_cipher_free(client_entry->send_key);
658   if (client_entry->receive_key)
659     silc_cipher_free(client_entry->receive_key);
660   silc_free(client_entry->key);
661   silc_client_ftp_session_free_client(conn, client_entry);
662   if (client_entry->ke)
663     silc_client_abort_key_agreement(client, conn, client_entry);
664   silc_free(client_entry);
665 }
666
667 /* Removes client from the cache by the client entry. */
668
669 bool silc_client_del_client(SilcClient client, SilcClientConnection conn,
670                             SilcClientEntry client_entry)
671 {
672   bool ret = silc_idcache_del_by_context(conn->client_cache, client_entry);
673
674   /* Remove from channels */
675   silc_client_remove_from_channels(client, conn, client_entry);
676
677   /* Free the client entry data */
678   silc_client_del_client_entry(client, conn, client_entry);
679
680   return ret;
681 }
682
683 /* Removes channel from the cache by the channel entry. */
684
685 bool silc_client_del_channel(SilcClient client, SilcClientConnection conn,
686                              SilcChannelEntry channel)
687 {
688   bool ret = silc_idcache_del_by_context(conn->channel_cache, channel);
689   silc_free(channel->channel_name);
690   silc_free(channel->id);
691   silc_free(channel->key);
692   if (channel->channel_key)
693     silc_cipher_free(channel->channel_key);
694   if (channel->hmac)
695     silc_hmac_free(channel->hmac);
696   if (channel->old_channel_key)
697     silc_cipher_free(channel->old_channel_key);
698   if (channel->old_hmac)
699     silc_hmac_free(channel->old_hmac);
700   if (channel->rekey_task)
701     silc_schedule_task_del(conn->client->schedule, channel->rekey_task);
702   silc_client_del_channel_private_keys(client, conn, channel);
703   silc_free(channel);
704   return ret;
705 }
706
707 /* Finds entry for channel by the channel name. Returns the entry or NULL
708    if the entry was not found. It is found only if the client is joined
709    to the channel. */
710
711 SilcChannelEntry silc_client_get_channel(SilcClient client,
712                                          SilcClientConnection conn,
713                                          char *channel)
714 {
715   SilcIDCacheEntry id_cache;
716   SilcChannelEntry entry;
717
718   SILC_LOG_DEBUG(("Start"));
719
720   if (!silc_idcache_find_by_name_one(conn->channel_cache, channel, 
721                                      &id_cache))
722     return NULL;
723
724   entry = (SilcChannelEntry)id_cache->context;
725
726   SILC_LOG_DEBUG(("Found"));
727
728   return entry;
729 }
730
731 /* Finds entry for channel by the channel ID. 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_by_id(SilcClient client,
736                                                SilcClientConnection conn,
737                                                SilcChannelID *channel_id)
738 {
739   SilcIDCacheEntry id_cache;
740   SilcChannelEntry entry;
741
742   SILC_LOG_DEBUG(("Start"));
743
744   if (!silc_idcache_find_by_id_one(conn->channel_cache, channel_id, 
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 typedef struct {
756   SilcClient client;
757   SilcClientConnection conn;
758   SilcChannelID *channel_id;
759   SilcGetChannelCallback completion;
760   void *context;
761 } *GetChannelByIDInternal;
762
763 SILC_CLIENT_CMD_FUNC(get_channel_by_id_callback)
764 {
765   GetChannelByIDInternal i = (GetChannelByIDInternal)context;
766   SilcChannelEntry entry;
767
768   SILC_LOG_DEBUG(("Start"));
769
770   /* Get the channel */
771   entry = silc_client_get_channel_by_id(i->client, i->conn, i->channel_id);
772   if (entry) {
773     i->completion(i->client, i->conn, &entry, 1, i->context);
774   } else {
775     i->completion(i->client, i->conn, NULL, 0, i->context);
776   }
777
778   silc_free(i->channel_id);
779   silc_free(i);
780 }
781
782 /* Resolves channel information from the server by the channel ID. */
783
784 void silc_client_get_channel_by_id_resolve(SilcClient client,
785                                            SilcClientConnection conn,
786                                            SilcChannelID *channel_id,
787                                            SilcGetChannelCallback completion,
788                                            void *context)
789 {
790   SilcBuffer idp;
791   GetChannelByIDInternal i = silc_calloc(1, sizeof(*i));
792
793   SILC_LOG_DEBUG(("Start"));
794
795   i->client = client;
796   i->conn = conn;
797   i->channel_id = silc_id_dup(channel_id, SILC_ID_CHANNEL);
798   i->completion = completion;
799   i->context = context;
800       
801   /* Register our own command reply for this command */
802   silc_client_command_register(client, SILC_COMMAND_IDENTIFY, NULL, NULL,
803                                silc_client_command_reply_identify_i, 0,
804                                ++conn->cmd_ident);
805
806   /* Send the command */
807   idp = silc_id_payload_encode(channel_id, SILC_ID_CHANNEL);
808   silc_client_command_send(client, conn, SILC_COMMAND_IDENTIFY, 
809                            conn->cmd_ident,
810                            1, 5, idp->data, idp->len);
811   silc_buffer_free(idp);
812
813   /* Add pending callback */
814   silc_client_command_pending(conn, SILC_COMMAND_IDENTIFY, conn->cmd_ident,
815                               silc_client_command_get_channel_by_id_callback, 
816                               (void *)i);
817 }
818
819 /* Find channel entry by ID. This routine is used internally by the library. */
820
821 SilcChannelEntry silc_idlist_get_channel_by_id(SilcClient client,
822                                                SilcClientConnection conn,
823                                                SilcChannelID *channel_id,
824                                                int query)
825 {
826   SilcBuffer idp;
827   SilcChannelEntry channel;
828
829   SILC_LOG_DEBUG(("Start"));
830
831   channel = silc_client_get_channel_by_id(client, conn, channel_id);
832   if (channel)
833     return channel;
834
835   if (query) {
836     /* Register our own command reply for this command */
837     silc_client_command_register(client, SILC_COMMAND_IDENTIFY, NULL, NULL,
838                                  silc_client_command_reply_identify_i, 0,
839                                  ++conn->cmd_ident);
840
841     /* Send the command */
842     idp = silc_id_payload_encode(channel_id, SILC_ID_CHANNEL);
843     silc_client_command_send(client, conn, SILC_COMMAND_IDENTIFY, 
844                              conn->cmd_ident,
845                              1, 5, idp->data, idp->len);
846     silc_buffer_free(idp);
847   }
848
849   return NULL;
850 }
851
852 /* Finds entry for server by the server name. */
853
854 SilcServerEntry silc_client_get_server(SilcClient client,
855                                        SilcClientConnection conn,
856                                        char *server_name)
857 {
858   SilcIDCacheEntry id_cache;
859   SilcServerEntry entry;
860
861   SILC_LOG_DEBUG(("Start"));
862
863   if (!silc_idcache_find_by_name_one(conn->server_cache, server_name, 
864                                      &id_cache))
865     return NULL;
866
867   entry = (SilcServerEntry)id_cache->context;
868
869   return entry;
870 }
871
872 /* Finds entry for server by the server ID. */
873
874 SilcServerEntry silc_client_get_server_by_id(SilcClient client,
875                                              SilcClientConnection conn,
876                                              SilcServerID *server_id)
877 {
878   SilcIDCacheEntry id_cache;
879   SilcServerEntry entry;
880
881   SILC_LOG_DEBUG(("Start"));
882
883   if (!silc_idcache_find_by_id_one(conn->server_cache, (void *)server_id, 
884                                    &id_cache))
885     return NULL;
886
887   entry = (SilcServerEntry)id_cache->context;
888
889   return entry;
890 }
891
892 /* Removes server from the cache by the server entry. */
893
894 bool silc_client_del_server(SilcClient client, SilcClientConnection conn,
895                             SilcServerEntry server)
896 {
897   bool ret = silc_idcache_del_by_context(conn->server_cache, server);
898   silc_free(server->server_name);
899   silc_free(server->server_info);
900   silc_free(server->server_id);
901   silc_free(server);
902   return ret;
903 }
904
905 /* Formats the nickname of the client specified by the `client_entry'.
906    If the format is specified by the application this will format the
907    nickname and replace the old nickname in the client entry. If the
908    format string is not specified then this function has no effect. */
909
910 void silc_client_nickname_format(SilcClient client, 
911                                  SilcClientConnection conn,
912                                  SilcClientEntry client_entry)
913 {
914   char *cp;
915   char *newnick = NULL;
916   int i, off = 0, len;
917   SilcClientEntry *clients;
918   uint32 clients_count = 0;
919   SilcClientEntry unformatted = NULL;
920
921   SILC_LOG_DEBUG(("Start"));
922
923   if (!client->internal->params->nickname_format[0])
924     return;
925
926   if (!client_entry->nickname)
927     return;
928
929   /* Get all clients with same nickname. Do not perform the formatting
930      if there aren't any clients with same nickname unless the application
931      is forcing us to do so. */
932   clients = silc_client_get_clients_local(client, conn,
933                                           client_entry->nickname, NULL,
934                                           &clients_count);
935   if (!clients && !client->internal->params->nickname_force_format)
936     return;
937
938   len = 0;
939   for (i = 0; i < clients_count; i++)
940     if (clients[i]->valid && clients[i] != client_entry)
941       len++;
942   if (!len)
943     return;
944
945   cp = client->internal->params->nickname_format;
946   while (*cp) {
947     if (*cp == '%') {
948       cp++;
949       continue;
950     }
951
952     switch(*cp) {
953     case 'n':
954       /* Nickname */
955       if (!client_entry->nickname)
956         break;
957       len = strlen(client_entry->nickname);
958       newnick = silc_realloc(newnick, sizeof(*newnick) * (off + len));
959       memcpy(&newnick[off], client_entry->nickname, len);
960       off += len;
961       break;
962     case 'h':
963       /* Stripped hostname */
964       if (!client_entry->hostname)
965         break;
966       len = strcspn(client_entry->hostname, ".");
967       newnick = silc_realloc(newnick, sizeof(*newnick) * (off + len));
968       memcpy(&newnick[off], client_entry->hostname, len);
969       off += len;
970       break;
971     case 'H':
972       /* Full hostname */
973       if (!client_entry->hostname)
974         break;
975       len = strlen(client_entry->hostname);
976       newnick = silc_realloc(newnick, sizeof(*newnick) * (off + len));
977       memcpy(&newnick[off], client_entry->hostname, len);
978       off += len;
979       break;
980     case 's':
981       /* Stripped server name */
982       if (!client_entry->server)
983         break;
984       len = strcspn(client_entry->server, ".");
985       newnick = silc_realloc(newnick, sizeof(*newnick) * (off + len));
986       memcpy(&newnick[off], client_entry->server, len);
987       off += len;
988       break;
989     case 'S':
990       /* Full server name */
991       if (!client_entry->server)
992         break;
993       len = strlen(client_entry->server);
994       newnick = silc_realloc(newnick, sizeof(*newnick) * (off + len));
995       memcpy(&newnick[off], client_entry->server, len);
996       off += len;
997       break;
998     case 'a':
999       /* Ascending number */
1000       {
1001         char tmp[6];
1002         int num, max = 1;
1003
1004         if (clients_count == 1) {
1005           unformatted = clients[0];
1006           break;
1007         }
1008
1009         for (i = 0; i < clients_count; i++) {
1010           if (!strncasecmp(clients[i]->nickname, client_entry->nickname,
1011                            strlen(clients[i]->nickname)))
1012             unformatted = clients[i];
1013           if (strncasecmp(clients[i]->nickname, newnick, off))
1014             continue;
1015           if (strlen(clients[i]->nickname) <= off)
1016             continue;
1017           num = atoi(&clients[i]->nickname[off]);
1018           if (num > max)
1019             max = num;
1020         }
1021
1022         memset(tmp, 0, sizeof(tmp));
1023         snprintf(tmp, sizeof(tmp) - 1, "%d", ++max);
1024         len = strlen(tmp);
1025         newnick = silc_realloc(newnick, sizeof(*newnick) * (off + len));
1026         memcpy(&newnick[off], tmp, len);
1027         off += len;
1028       }
1029       break;
1030     default:
1031       /* Some other character in the string */
1032       newnick = silc_realloc(newnick, sizeof(*newnick) * (off + 1));
1033       memcpy(&newnick[off], cp, 1);
1034       off++;
1035       break;
1036     }
1037
1038     cp++;
1039   }
1040
1041   newnick = silc_realloc(newnick, sizeof(*newnick) * (off + 1));
1042   newnick[off] = 0;
1043
1044   /* If we are changing nickname of our local entry we'll enforce
1045      that we will always get the unformatted nickname.  Give our
1046      format number to the one that is not formatted now. */
1047   if (unformatted && client_entry == conn->local_entry)
1048     client_entry = unformatted;
1049
1050   silc_free(client_entry->nickname);
1051   client_entry->nickname = newnick;
1052   silc_free(clients);
1053 }