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