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