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