Added SILC Server library.
[silc.git] / lib / silcclient / idlist.c
1 /*
2
3   idlist.c
4
5   Author: Pekka Riikonen <priikone@silcnet.org>
6
7   Copyright (C) 2001 - 2005 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; version 2 of the License.
12
13   This program is distributed in the hope that it will be useful,
14   but WITHOUT ANY WARRANTY; without even the implied warranty of
15   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16   GNU General Public License for more details.
17
18 */
19 /* $Id$ */
20
21 #include "silc.h"
22 #include "silcclient.h"
23 #include "client_internal.h"
24
25 /******************************************************************************
26
27                          Client Searching Locally
28
29 ******************************************************************************/
30
31 /* Same as silc_client_get_clients function but does not resolve anything
32    from the server. This checks local cache and returns all matching
33    clients from the local cache. If none was found this returns NULL.
34    The `nickname' is the real nickname of the client, and the `format'
35    is the formatted nickname to find exact match from multiple found
36    entries. The format must be same as given in the SilcClientParams
37    structure to the client library. If the `format' is NULL all found
38    clients by `nickname' are returned. */
39
40 SilcClientEntry *silc_client_get_clients_local(SilcClient client,
41                                                SilcClientConnection conn,
42                                                const char *nickname,
43                                                const char *format,
44                                                SilcUInt32 *clients_count)
45 {
46   SilcIDCacheEntry id_cache;
47   SilcIDCacheList list = NULL;
48   SilcClientEntry entry, *clients;
49   int i = 0;
50   SilcBool found = FALSE;
51   char *nicknamec;
52
53   assert(client && conn);
54   if (!nickname)
55     return NULL;
56
57   /* Normalize nickname for search */
58   nicknamec = silc_identifier_check(nickname, strlen(nickname),
59                                     SILC_STRING_UTF8, 128, NULL);
60   if (!nicknamec)
61     return NULL;
62
63   /* Find ID from cache */
64   if (!silc_idcache_find_by_name(conn->internal->client_cache, nicknamec,
65                                  &list)) {
66     silc_free(nicknamec);
67     return NULL;
68   }
69
70   if (!silc_idcache_list_count(list)) {
71     silc_idcache_list_free(list);
72     silc_free(nicknamec);
73     return NULL;
74   }
75
76   clients = silc_calloc(silc_idcache_list_count(list), sizeof(*clients));
77   *clients_count = silc_idcache_list_count(list);
78
79   if (!format) {
80     /* Take all without any further checking */
81     silc_idcache_list_first(list, &id_cache);
82     while (id_cache) {
83       clients[i++] = id_cache->context;
84       found = TRUE;
85       if (!silc_idcache_list_next(list, &id_cache))
86         break;
87     }
88   } else {
89     /* Check multiple cache entries for match */
90     silc_idcache_list_first(list, &id_cache);
91     while (id_cache) {
92       entry = (SilcClientEntry)id_cache->context;
93       if (!silc_utf8_strcasecmp(entry->nickname, format)) {
94         if (!silc_idcache_list_next(list, &id_cache)) {
95           break;
96         } else {
97           continue;
98         }
99       }
100
101       clients[i++] = id_cache->context;
102       found = TRUE;
103       if (!silc_idcache_list_next(list, &id_cache))
104         break;
105     }
106   }
107
108   silc_free(nicknamec);
109
110   if (list)
111     silc_idcache_list_free(list);
112
113   if (!found) {
114     *clients_count = 0;
115     if (clients)
116       silc_free(clients);
117     return NULL;
118   }
119
120   return clients;
121 }
122
123
124 /******************************************************************************
125
126                         Client Resolving from Server
127
128 ******************************************************************************/
129
130 typedef struct {
131   SilcClient client;
132   SilcClientConnection conn;
133   SilcGetClientCallback completion;
134   void *context;
135   char *nickname;
136   SilcClientEntry *clients;
137   SilcUInt32 clients_count;
138 } *GetClientInternal;
139
140 /* Completion for IDENTIFY */
141
142 SILC_CLIENT_CMD_FUNC(get_client_callback)
143 {
144   GetClientInternal i = (GetClientInternal)context;
145   SilcClientEntry *clients;
146   SilcUInt32 clients_count;
147
148   /* Get the clients */
149   clients = silc_client_get_clients_local(i->client, i->conn,
150                                           i->nickname, NULL,
151                                           &clients_count);
152   if (clients) {
153     i->completion(i->client, i->conn, clients, clients_count, i->context);
154     silc_free(clients);
155   } else {
156     i->completion(i->client, i->conn, NULL, 0, i->context);
157   }
158
159   silc_free(i->nickname);
160   silc_free(i);
161 }
162
163 /* Completion for WHOIS */
164
165 SILC_CLIENT_CMD_FUNC(get_client_callback_wc)
166 {
167   GetClientInternal i = (GetClientInternal)context;
168   SilcClientCommandReplyContext cmd = context2;
169   SilcClientID *client_id = NULL;
170   SilcClientEntry client_entry = NULL;
171   unsigned char *id_data;
172   SilcUInt32 len;
173
174   /* Get the client entry just returned from server */
175   id_data = silc_argument_get_arg_type(cmd->args, 2, &len);
176   if (id_data)
177     client_id = silc_id_payload_parse_id(id_data, len, NULL);
178   if (client_id)
179     client_entry = silc_client_get_client_by_id(i->client,
180                                                 i->conn, client_id);
181   if (!client_entry) {
182     if (!SILC_STATUS_IS_ERROR(cmd->status) &&
183         cmd->status != SILC_STATUS_OK &&
184         cmd->status != SILC_STATUS_LIST_END) {
185       silc_free(client_id);
186       return;
187     }
188
189     i->completion(i->client, i->conn, i->clients, i->clients_count,
190                   i->context);
191     silc_free(client_id);
192     silc_free(i->clients);
193     silc_free(i->nickname);
194     silc_free(i);
195     return;
196   }
197
198   /* Save the client */
199   i->clients = silc_realloc(i->clients,
200                             (sizeof(*i->clients) * (i->clients_count + 1)));
201   i->clients[i->clients_count] = client_entry;
202   i->clients_count++;
203
204   /* Return if more data is expected */
205   if (cmd->status != SILC_STATUS_OK &&
206       cmd->status != SILC_STATUS_LIST_END) {
207     silc_free(client_id);
208     return;
209   }
210
211   i->completion(i->client, i->conn, i->clients, i->clients_count,
212                 i->context);
213
214   silc_free(client_id);
215   silc_free(i->clients);
216   silc_free(i->nickname);
217   silc_free(i);
218 }
219
220 /* Our own WHOIS reply processor. */
221
222 SILC_CLIENT_CMD_FUNC(get_client_callback_w)
223 {
224   SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
225   SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
226
227   SILC_LOG_DEBUG(("Start"));
228
229   if (!silc_command_get_status(cmd->payload, NULL, NULL)) {
230     if (SILC_STATUS_IS_ERROR(cmd->status))
231       goto out;
232     if (cmd->status == SILC_STATUS_LIST_END)
233       goto out;
234     goto err;
235   }
236
237   /* Save WHOIS info */
238   silc_client_command_reply_whois_save(cmd, cmd->status, FALSE);
239
240   /* Call pending completion for each reply */
241   if (cmd->status != SILC_STATUS_OK &&
242       cmd->status != SILC_STATUS_LIST_END) {
243     if (cmd->callbacks[0].callback)
244       (*cmd->callbacks[0].callback)(cmd->callbacks[0].context, cmd);
245     silc_client_command_reply_free(cmd);
246     return;
247   }
248
249  out:
250   SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_WHOIS);
251
252  err:
253   /* If we received notify for invalid ID we'll remove the ID if we
254      have it cached. */
255   if (cmd->error == SILC_STATUS_ERR_NO_SUCH_CLIENT_ID) {
256     SilcClientEntry client_entry;
257     SilcUInt32 tmp_len;
258     unsigned char *tmp =
259       silc_argument_get_arg_type(silc_command_get_args(cmd->payload),
260                                  2, &tmp_len);
261     if (tmp) {
262       SilcClientID *client_id = silc_id_payload_parse_id(tmp, tmp_len, NULL);
263       if (client_id) {
264         client_entry = silc_client_get_client_by_id(cmd->client, conn,
265                                                     client_id);
266         if (client_entry)
267           silc_client_del_client(cmd->client, conn, client_entry);
268         silc_free(client_id);
269       }
270     }
271   }
272
273   /* Unregister this command reply */
274   silc_client_command_unregister(cmd->client, SILC_COMMAND_WHOIS,
275                                  NULL, silc_client_command_reply_whois_i,
276                                  cmd->ident);
277   silc_client_command_reply_free(cmd);
278 }
279
280 /* Finds client entry or entries by the `nickname' and `server'. The
281    completion callback will be called when the client entries has been found.
282
283    Note: this function is always asynchronous and resolves the client
284    information from the server. Thus, if you already know the client
285    information then use the silc_client_get_client_by_id function to
286    get the client entry since this function may be very slow and should
287    be used only to initially get the client entries. */
288
289 void silc_client_get_clients_i(SilcClient client,
290                                SilcClientConnection conn,
291                                SilcCommand command,
292                                const char *nickname,
293                                const char *server,
294                                SilcBuffer attributes,
295                                SilcGetClientCallback completion,
296                                void *context)
297 {
298   GetClientInternal i;
299   int len;
300   char *userhost = NULL;
301
302   assert(client && conn);
303
304   if (!nickname && !attributes)
305     return;
306
307   i = silc_calloc(1, sizeof(*i));
308   i->client = client;
309   i->conn = conn;
310   i->nickname = nickname ? strdup(nickname) : NULL;
311   i->completion = completion;
312   i->context = context;
313
314   if (nickname && server) {
315     len = strlen(nickname) + strlen(server) + 3;
316     userhost = silc_calloc(len, sizeof(*userhost));
317     silc_strncat(userhost, len, nickname, strlen(nickname));
318     silc_strncat(userhost, len, "@", 1);
319     silc_strncat(userhost, len, server, strlen(server));
320   } else if (nickname) {
321     userhost = silc_memdup(nickname, strlen(nickname));
322   }
323
324   /* Register our own command reply for this command */
325   if (command == SILC_COMMAND_IDENTIFY) {
326     silc_client_command_register(client, command, NULL, NULL,
327                                  silc_client_command_reply_identify_i, 0,
328                                  ++conn->cmd_ident);
329     /* Send the command */
330     silc_client_command_send(client, conn, SILC_COMMAND_IDENTIFY,
331                              conn->cmd_ident, 1, 1, userhost,
332                              strlen(userhost));
333
334     /* Add pending callback */
335     silc_client_command_pending(conn, command, conn->cmd_ident,
336                                 silc_client_command_get_client_callback,
337                                 (void *)i);
338   } else {
339     silc_client_command_register(client, command, NULL, NULL,
340                                  silc_client_command_get_client_callback_w, 0,
341                                  ++conn->cmd_ident);
342     /* Send the command */
343     silc_client_command_send(client, conn, command, conn->cmd_ident, 2,
344                              1, userhost, userhost ? strlen(userhost) : 0,
345                              3, attributes ? attributes->data : NULL,
346                              attributes ? attributes->len : 0);
347
348     /* Add pending callback */
349     silc_client_command_pending(conn, command, conn->cmd_ident,
350                                 silc_client_command_get_client_callback_wc,
351                                 (void *)i);
352   }
353   silc_free(userhost);
354 }
355
356 void silc_client_get_clients(SilcClient client,
357                              SilcClientConnection conn,
358                              const char *nickname,
359                              const char *server,
360                              SilcGetClientCallback completion,
361                              void *context)
362 {
363   silc_client_get_clients_i(client, conn, SILC_COMMAND_IDENTIFY,
364                             nickname, server, NULL,
365                             completion, context);
366 }
367
368 void silc_client_get_clients_whois(SilcClient client,
369                                    SilcClientConnection conn,
370                                    const char *nickname,
371                                    const char *server,
372                                    SilcBuffer attributes,
373                                    SilcGetClientCallback completion,
374                                    void *context)
375 {
376   silc_client_get_clients_i(client, conn, SILC_COMMAND_WHOIS,
377                             nickname, server, attributes,
378                             completion, context);
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 /* XXX This function should be removed */
386
387 SilcClientEntry silc_idlist_get_client(SilcClient client,
388                                        SilcClientConnection conn,
389                                        const char *nickname,
390                                        const char *format,
391                                        SilcBool query)
392 {
393   SilcIDCacheEntry id_cache;
394   SilcIDCacheList list = NULL;
395   SilcClientEntry entry = NULL;
396   char *nicknamec;
397
398   SILC_LOG_DEBUG(("Start"));
399
400   /* Normalize nickname for search */
401   nicknamec = silc_identifier_check(nickname, strlen(nickname),
402                                     SILC_STRING_UTF8, 128, NULL);
403   if (!nicknamec)
404     return NULL;
405
406   /* Find ID from cache */
407   if (!silc_idcache_find_by_name(conn->internal->client_cache,
408                                  nicknamec, &list)) {
409   identify:
410
411     if (query) {
412       SILC_LOG_DEBUG(("Requesting Client ID from server"));
413
414       /* Register our own command reply for this command */
415       silc_client_command_register(client, SILC_COMMAND_IDENTIFY, NULL, NULL,
416                                    silc_client_command_reply_identify_i, 0,
417                                    ++conn->cmd_ident);
418
419       /* Send the command */
420       silc_client_command_send(client, conn, SILC_COMMAND_IDENTIFY,
421                                conn->cmd_ident, 1, 1, nickname,
422                                strlen(nickname));
423
424       if (list)
425         silc_idcache_list_free(list);
426
427       silc_free(nicknamec);
428       return NULL;
429     }
430
431     silc_free(nicknamec);
432     return NULL;
433   }
434
435   if (!format) {
436     /* Take first found cache entry */
437     if (!silc_idcache_list_first(list, &id_cache))
438       goto identify;
439
440     entry = (SilcClientEntry)id_cache->context;
441   } else {
442     /* Check multiple cache entries for match */
443     silc_idcache_list_first(list, &id_cache);
444     while (id_cache) {
445       entry = (SilcClientEntry)id_cache->context;
446
447       if (!silc_utf8_strcasecmp(entry->nickname, format)) {
448         if (!silc_idcache_list_next(list, &id_cache)) {
449           entry = NULL;
450           break;
451         } else {
452           entry = NULL;
453           continue;
454         }
455       }
456
457       break;
458     }
459
460     /* If match weren't found, request it */
461     if (!entry)
462       goto identify;
463   }
464
465   silc_free(nicknamec);
466
467   if (list)
468     silc_idcache_list_free(list);
469
470   return entry;
471 }
472
473 typedef struct {
474   SilcClient client;
475   SilcClientConnection conn;
476   SilcUInt32 list_count;
477   SilcBuffer client_id_list;
478   SilcGetClientCallback completion;
479   void *context;
480   int res_count;
481 } *GetClientsByListInternal;
482
483 SILC_CLIENT_CMD_FUNC(get_clients_list_callback)
484 {
485   GetClientsByListInternal i = (GetClientsByListInternal)context;
486   SilcIDCacheEntry id_cache = NULL;
487   SilcBuffer client_id_list = i->client_id_list;
488   SilcClientEntry *clients = NULL;
489   SilcUInt32 clients_count = 0;
490   SilcBool found = FALSE;
491   int c;
492
493   SILC_LOG_DEBUG(("Start"));
494
495   if (i->res_count) {
496     i->res_count--;
497     if (i->res_count)
498       return;
499   }
500
501   SILC_LOG_DEBUG(("Resolved all clients"));
502
503   clients = silc_calloc(i->list_count, sizeof(*clients));
504
505   for (c = 0; c < i->list_count; c++) {
506     SilcUInt16 idp_len;
507     SilcClientID *client_id;
508
509     /* Get Client ID */
510     SILC_GET16_MSB(idp_len, client_id_list->data + 2);
511     idp_len += 4;
512     client_id = silc_id_payload_parse_id(client_id_list->data, idp_len, NULL);
513     if (!client_id) {
514       silc_buffer_pull(client_id_list, idp_len);
515       continue;
516     }
517
518     /* Get the client entry */
519     if (silc_idcache_find_by_id_one_ext(i->conn->internal->client_cache,
520                                         (void *)client_id,
521                                         NULL, NULL,
522                                         silc_hash_client_id_compare, NULL,
523                                         &id_cache)) {
524       clients[clients_count] = (SilcClientEntry)id_cache->context;
525       clients_count++;
526       found = TRUE;
527     }
528
529     silc_free(client_id);
530     silc_buffer_pull(client_id_list, idp_len);
531   }
532
533   if (found) {
534     i->completion(i->client, i->conn, clients, clients_count, i->context);
535     silc_free(clients);
536   } else {
537     i->completion(i->client, i->conn, NULL, 0, i->context);
538   }
539
540   if (i->client_id_list)
541     silc_buffer_free(i->client_id_list);
542   silc_free(i);
543 }
544
545 /* Gets client entries by the list of client ID's `client_id_list'. This
546    always resolves those client ID's it does not know yet from the server
547    so this function might take a while. The `client_id_list' is a list
548    of ID Payloads added one after other.  JOIN command reply and USERS
549    command reply for example returns this sort of list. The `completion'
550    will be called after the entries are available. */
551
552 void silc_client_get_clients_by_list(SilcClient client,
553                                      SilcClientConnection conn,
554                                      SilcUInt32 list_count,
555                                      SilcBuffer client_id_list,
556                                      SilcGetClientCallback completion,
557                                      void *context)
558 {
559   SilcIDCacheEntry id_cache = NULL;
560   int i;
561   unsigned char **res_argv = NULL;
562   SilcUInt32 *res_argv_lens = NULL, *res_argv_types = NULL, res_argc = 0;
563   GetClientsByListInternal in;
564   SilcBool wait_res = FALSE;
565
566   assert(client && conn && client_id_list);
567
568   SILC_LOG_DEBUG(("Start"));
569
570   in = silc_calloc(1, sizeof(*in));
571   in->client = client;
572   in->conn = conn;
573   in->list_count = list_count;
574   in->client_id_list = silc_buffer_copy(client_id_list);
575   in->completion = completion;
576   in->context = context;
577
578   for (i = 0; i < list_count; i++) {
579     SilcUInt16 idp_len;
580     SilcClientID *client_id;
581     SilcClientEntry entry;
582     SilcBool ret;
583
584     /* Get Client ID */
585     SILC_GET16_MSB(idp_len, client_id_list->data + 2);
586     idp_len += 4;
587     client_id = silc_id_payload_parse_id(client_id_list->data, idp_len, NULL);
588     if (!client_id) {
589       silc_buffer_pull(client_id_list, idp_len);
590       continue;
591     }
592
593     /* Check if we have this client cached already. */
594     ret =
595       silc_idcache_find_by_id_one_ext(conn->internal->client_cache,
596                                       (void *)client_id, NULL, NULL,
597                                       silc_hash_client_id_compare, NULL,
598                                       &id_cache);
599
600     /* If we don't have the entry or it has incomplete info, then resolve
601        it from the server. */
602     if (!ret || !((SilcClientEntry)id_cache->context)->nickname) {
603       entry = ret ? (SilcClientEntry)id_cache->context : NULL;
604
605       if (entry) {
606         if (entry->status & SILC_CLIENT_STATUS_RESOLVING) {
607           /* Attach to this resolving and wait until it finishes */
608           silc_client_command_pending(
609                             conn, SILC_COMMAND_NONE,
610                             entry->resolve_cmd_ident,
611                             silc_client_command_get_clients_list_callback,
612                             (void *)in);
613           wait_res = TRUE;
614           in->res_count++;
615
616           silc_free(client_id);
617           silc_buffer_pull(client_id_list, idp_len);
618           continue;
619         }
620
621         entry->status |= SILC_CLIENT_STATUS_RESOLVING;
622         entry->resolve_cmd_ident = conn->cmd_ident + 1;
623       }
624
625       /* No we don't have it, query it from the server. Assemble argument
626          table that will be sent for the IDENTIFY command later. */
627       res_argv = silc_realloc(res_argv, sizeof(*res_argv) *
628                               (res_argc + 1));
629       res_argv_lens = silc_realloc(res_argv_lens, sizeof(*res_argv_lens) *
630                                    (res_argc + 1));
631       res_argv_types = silc_realloc(res_argv_types, sizeof(*res_argv_types) *
632                                     (res_argc + 1));
633       res_argv[res_argc] = client_id_list->data;
634       res_argv_lens[res_argc] = idp_len;
635       res_argv_types[res_argc] = res_argc + 5;
636       res_argc++;
637     }
638
639     silc_free(client_id);
640     silc_buffer_pull(client_id_list, idp_len);
641   }
642
643   silc_buffer_push(client_id_list, client_id_list->data -
644                    client_id_list->head);
645
646   /* Query the client information from server if the list included clients
647      that we don't know about. */
648   if (res_argc) {
649     SilcBuffer res_cmd;
650
651     /* Send the IDENTIFY command to server */
652     res_cmd = silc_command_payload_encode(SILC_COMMAND_IDENTIFY,
653                                           res_argc, res_argv, res_argv_lens,
654                                           res_argv_types, ++conn->cmd_ident);
655     silc_client_packet_send(client, conn->sock, SILC_PACKET_COMMAND,
656                             NULL, 0, NULL, NULL, res_cmd->data, res_cmd->len,
657                             TRUE);
658
659     /* Register our own command reply for this command */
660     silc_client_command_register(client, SILC_COMMAND_IDENTIFY, NULL, NULL,
661                                  silc_client_command_reply_identify_i, 0,
662                                  conn->cmd_ident);
663
664     /* Process the applications request after reply has been received  */
665     silc_client_command_pending(conn, SILC_COMMAND_IDENTIFY, conn->cmd_ident,
666                                 silc_client_command_get_clients_list_callback,
667                                 (void *)in);
668     in->res_count++;
669
670     silc_buffer_free(res_cmd);
671     silc_free(res_argv);
672     silc_free(res_argv_lens);
673     silc_free(res_argv_types);
674     return;
675   }
676
677   if (wait_res)
678     return;
679
680   /* We have the clients in cache, get them and call the completion */
681   silc_client_command_get_clients_list_callback((void *)in, NULL);
682 }
683
684 typedef struct {
685   SilcClient client;
686   SilcClientConnection conn;
687   SilcChannelID channel_id;
688   SilcGetClientCallback completion;
689   void *context;
690   int res_count;
691 } *GetClientsByChannelInternal;
692
693 SILC_CLIENT_CMD_FUNC(get_clients_by_channel_cb)
694 {
695   GetClientsByChannelInternal i = context;
696   SilcClientEntry *clients = NULL;
697   SilcUInt32 clients_count = 0;
698   SilcBool found = FALSE;
699   SilcChannelEntry channel;
700   SilcHashTableList htl;
701   SilcChannelUser chu;
702
703   if (i->res_count) {
704     i->res_count--;
705     if (i->res_count)
706       return;
707   }
708
709   channel = silc_client_get_channel_by_id(i->client, i->conn, &i->channel_id);
710   if (channel && !silc_hash_table_count(channel->user_list)) {
711     clients = silc_calloc(silc_hash_table_count(channel->user_list),
712                           sizeof(*clients));
713     silc_hash_table_list(channel->user_list, &htl);
714     while (silc_hash_table_get(&htl, NULL, (void *)&chu))
715       clients[clients_count++] = chu->client;
716     silc_hash_table_list_reset(&htl);
717     found = TRUE;
718   }
719
720   if (found) {
721     i->completion(i->client, i->conn, clients, clients_count, i->context);
722     silc_free(clients);
723   } else {
724     i->completion(i->client, i->conn, NULL, 0, i->context);
725   }
726
727   silc_free(i);
728 }
729
730 /* Gets client entries by the channel entry indicated by `channel'.  Thus,
731    it resolves the clients currently on that channel. */
732
733 void silc_client_get_clients_by_channel(SilcClient client,
734                                         SilcClientConnection conn,
735                                         SilcChannelEntry channel,
736                                         SilcGetClientCallback completion,
737                                         void *context)
738 {
739   GetClientsByChannelInternal in;
740   SilcHashTableList htl;
741   SilcChannelUser chu;
742   SilcClientEntry entry;
743   unsigned char **res_argv = NULL;
744   SilcUInt32 *res_argv_lens = NULL, *res_argv_types = NULL, res_argc = 0;
745   SilcBuffer idp;
746   SilcBool wait_res = FALSE;
747
748   assert(client && conn && channel);
749
750   SILC_LOG_DEBUG(("Start"));
751
752   in = silc_calloc(1, sizeof(*in));
753   in->client = client;
754   in->conn = conn;
755   in->channel_id = *channel->id;
756   in->completion = completion;
757   in->context = context;
758
759   /* If user list does not exist, send USERS command. */
760   if (!channel->user_list || !silc_hash_table_count(channel->user_list)) {
761     SILC_LOG_DEBUG(("Sending USERS"));
762     silc_client_command_register(client, SILC_COMMAND_USERS, NULL, NULL,
763                                  silc_client_command_reply_users_i, 0,
764                                  ++conn->cmd_ident);
765     silc_client_command_send(client, conn, SILC_COMMAND_USERS,
766                              conn->cmd_ident, 1, 2, channel->channel_name,
767                              strlen(channel->channel_name));
768     silc_client_command_pending(conn, SILC_COMMAND_USERS, conn->cmd_ident,
769                                 silc_client_command_get_clients_by_channel_cb,
770                                 in);
771     return;
772   }
773
774   silc_hash_table_list(channel->user_list, &htl);
775   while (silc_hash_table_get(&htl, NULL, (void *)&chu)) {
776     entry = chu->client;
777
778     /* If the entry has incomplete info, then resolve it from the server. */
779     if (!entry->nickname || !entry->realname) {
780       if (entry->status & SILC_CLIENT_STATUS_RESOLVING) {
781         /* Attach to this resolving and wait until it finishes */
782         silc_client_command_pending(
783                             conn, SILC_COMMAND_NONE,
784                             entry->resolve_cmd_ident,
785                             silc_client_command_get_clients_by_channel_cb,
786                             (void *)in);
787         wait_res = TRUE;
788         in->res_count++;
789         continue;
790       }
791       entry->status |= SILC_CLIENT_STATUS_RESOLVING;
792       entry->resolve_cmd_ident = conn->cmd_ident + 1;
793
794       idp = silc_id_payload_encode(entry->id, SILC_ID_CLIENT);
795
796       /* No we don't have it, query it from the server. Assemble argument
797          table that will be sent for the WHOIS command later. */
798       res_argv = silc_realloc(res_argv, sizeof(*res_argv) *
799                               (res_argc + 1));
800       res_argv_lens = silc_realloc(res_argv_lens, sizeof(*res_argv_lens) *
801                                    (res_argc + 1));
802       res_argv_types = silc_realloc(res_argv_types, sizeof(*res_argv_types) *
803                                     (res_argc + 1));
804       res_argv[res_argc] = silc_memdup(idp->data, idp->len);
805       res_argv_lens[res_argc] = idp->len;
806       res_argv_types[res_argc] = res_argc + 4;
807       res_argc++;
808
809       silc_buffer_free(idp);
810     }
811   }
812   silc_hash_table_list_reset(&htl);
813
814   /* Query the client information from server if the list included clients
815      that we don't know about. */
816   if (res_argc) {
817     SilcBuffer res_cmd;
818
819     /* Send the WHOIS command to server */
820     res_cmd = silc_command_payload_encode(SILC_COMMAND_WHOIS,
821                                           res_argc, res_argv, res_argv_lens,
822                                           res_argv_types, ++conn->cmd_ident);
823     silc_client_packet_send(client, conn->sock, SILC_PACKET_COMMAND,
824                             NULL, 0, NULL, NULL, res_cmd->data, res_cmd->len,
825                             TRUE);
826
827     /* Register our own command reply for this command */
828     silc_client_command_register(client, SILC_COMMAND_WHOIS, NULL, NULL,
829                                  silc_client_command_reply_whois_i, 0,
830                                  conn->cmd_ident);
831
832     /* Process the applications request after reply has been received  */
833     silc_client_command_pending(
834                            conn, SILC_COMMAND_WHOIS, conn->cmd_ident,
835                            silc_client_command_get_clients_by_channel_cb,
836                            (void *)in);
837     in->res_count++;
838
839     silc_buffer_free(res_cmd);
840     silc_free(res_argv);
841     silc_free(res_argv_lens);
842     silc_free(res_argv_types);
843     return;
844   }
845
846   if (wait_res)
847     return;
848
849   /* We have the clients in cache, get them and call the completion */
850   silc_client_command_get_clients_by_channel_cb((void *)in, NULL);
851 }
852
853 /* Finds entry for client by the client's ID. Returns the entry or NULL
854    if the entry was not found. */
855
856 SilcClientEntry silc_client_get_client_by_id(SilcClient client,
857                                              SilcClientConnection conn,
858                                              SilcClientID *client_id)
859 {
860   SilcIDCacheEntry id_cache;
861
862   assert(client && conn);
863   if (!client_id)
864     return NULL;
865
866   SILC_LOG_DEBUG(("Finding client by ID (%s)",
867                   silc_id_render(client_id, SILC_ID_CLIENT)));
868
869   /* Find ID from cache */
870   if (!silc_idcache_find_by_id_one_ext(conn->internal->client_cache,
871                                        (void *)client_id, NULL, NULL,
872                                        silc_hash_client_id_compare, NULL,
873                                        &id_cache))
874     return NULL;
875
876   SILC_LOG_DEBUG(("Found"));
877
878   return (SilcClientEntry)id_cache->context;
879 }
880
881 typedef struct {
882   SilcClient client;
883   SilcClientConnection conn;
884   SilcClientID *client_id;
885   SilcGetClientCallback completion;
886   void *context;
887 } *GetClientByIDInternal;
888
889 SILC_CLIENT_CMD_FUNC(get_client_by_id_callback)
890 {
891   GetClientByIDInternal i = (GetClientByIDInternal)context;
892   SilcClientEntry entry;
893
894   /* Get the client */
895   entry = silc_client_get_client_by_id(i->client, i->conn, i->client_id);
896   if (entry) {
897     if (i->completion)
898       i->completion(i->client, i->conn, &entry, 1, i->context);
899   } else {
900     if (i->completion)
901       i->completion(i->client, i->conn, NULL, 0, i->context);
902   }
903
904   silc_free(i->client_id);
905   silc_free(i);
906 }
907
908 /* Same as above but will always resolve the information from the server.
909    Use this only if you know that you don't have the entry and the only
910    thing you know about the client is its ID. */
911
912 void silc_client_get_client_by_id_resolve(SilcClient client,
913                                           SilcClientConnection conn,
914                                           SilcClientID *client_id,
915                                           SilcBuffer attributes,
916                                           SilcGetClientCallback completion,
917                                           void *context)
918 {
919   SilcBuffer idp;
920   GetClientByIDInternal i = silc_calloc(1, sizeof(*i));
921
922   assert(client && conn && client_id);
923
924   SILC_LOG_DEBUG(("Start"));
925
926   i->client = client;
927   i->conn = conn;
928   i->client_id = silc_id_dup(client_id, SILC_ID_CLIENT);
929   i->completion = completion;
930   i->context = context;
931
932   /* Register our own command reply for this command */
933   silc_client_command_register(client, SILC_COMMAND_WHOIS, NULL, NULL,
934                                silc_client_command_reply_whois_i, 0,
935                                ++conn->cmd_ident);
936
937   /* Send the command */
938   idp = silc_id_payload_encode(client_id, SILC_ID_CLIENT);
939   silc_client_command_send(client, conn, SILC_COMMAND_WHOIS, conn->cmd_ident,
940                            2, 3, attributes ? attributes->data : NULL,
941                            attributes ? attributes->len : 0,
942                            4, idp->data, idp->len);
943   silc_buffer_free(idp);
944
945   /* Add pending callback */
946   silc_client_command_pending(conn, SILC_COMMAND_WHOIS, conn->cmd_ident,
947                               silc_client_command_get_client_by_id_callback,
948                               (void *)i);
949 }
950
951
952 /******************************************************************************
953
954                 Client, Channel and Server entry manipulation
955
956 ******************************************************************************/
957
958
959 /* Creates new client entry and adds it to the ID cache. Returns pointer
960    to the new entry. */
961
962 SilcClientEntry
963 silc_client_add_client(SilcClient client, SilcClientConnection conn,
964                        char *nickname, char *username,
965                        char *userinfo, SilcClientID *id, SilcUInt32 mode)
966 {
967   SilcClientEntry client_entry;
968   char *nick = NULL;
969
970   SILC_LOG_DEBUG(("Start"));
971
972   /* Save the client infos */
973   client_entry = silc_calloc(1, sizeof(*client_entry));
974   client_entry->id = id;
975   client_entry->valid = TRUE;
976   silc_parse_userfqdn(nickname, &nick, &client_entry->server);
977   silc_parse_userfqdn(username, &client_entry->username,
978                       &client_entry->hostname);
979   if (userinfo)
980     client_entry->realname = strdup(userinfo);
981   client_entry->mode = mode;
982   if (nick)
983     client_entry->nickname = strdup(nick);
984   client_entry->channels = silc_hash_table_alloc(1, silc_hash_ptr, NULL, NULL,
985                                                  NULL, NULL, NULL, TRUE);
986
987   /* Normalize nickname */
988   if (client_entry->nickname) {
989     silc_free(nick);
990     nick = silc_identifier_check(client_entry->nickname,
991                                  strlen(client_entry->nickname),
992                                  SILC_STRING_UTF8, 128, NULL);
993     if (!nick) {
994       silc_free(client_entry->nickname);
995       silc_free(client_entry->username);
996       silc_free(client_entry->hostname);
997       silc_free(client_entry->server);
998       silc_hash_table_free(client_entry->channels);
999       silc_free(client_entry);
1000       return NULL;
1001     }
1002   }
1003
1004   /* Format the nickname */
1005   silc_client_nickname_format(client, conn, client_entry);
1006
1007   /* Add client to cache, the non-formatted nickname is saved to cache */
1008   if (!silc_idcache_add(conn->internal->client_cache, nick, client_entry->id,
1009                         (void *)client_entry, 0, NULL)) {
1010     silc_free(nick);
1011     silc_free(client_entry->nickname);
1012     silc_free(client_entry->username);
1013     silc_free(client_entry->hostname);
1014     silc_free(client_entry->server);
1015     silc_hash_table_free(client_entry->channels);
1016     silc_free(client_entry);
1017     return NULL;
1018   }
1019
1020   return client_entry;
1021 }
1022
1023 /* Updates the `client_entry' with the new information sent as argument. */
1024
1025 void silc_client_update_client(SilcClient client,
1026                                SilcClientConnection conn,
1027                                SilcClientEntry client_entry,
1028                                const char *nickname,
1029                                const char *username,
1030                                const char *userinfo,
1031                                SilcUInt32 mode)
1032 {
1033   char *nick = NULL;
1034
1035   SILC_LOG_DEBUG(("Start"));
1036
1037   if ((!client_entry->username || !client_entry->hostname) && username) {
1038     silc_free(client_entry->username);
1039     silc_free(client_entry->hostname);
1040     client_entry->username = NULL;
1041     client_entry->hostname = NULL;
1042     silc_parse_userfqdn(username, &client_entry->username,
1043                         &client_entry->hostname);
1044   }
1045   if (!client_entry->realname && userinfo)
1046     client_entry->realname = strdup(userinfo);
1047   if (!client_entry->nickname && nickname) {
1048     silc_parse_userfqdn(nickname, &nick, &client_entry->server);
1049     client_entry->nickname = strdup(nick);
1050
1051     /* Normalize nickname */
1052     silc_free(nick);
1053     nick = silc_identifier_check(client_entry->nickname,
1054                                  strlen(client_entry->nickname),
1055                                  SILC_STRING_UTF8, 128, NULL);
1056     if (!nick)
1057       return;
1058
1059     /* Format nickname */
1060     silc_client_nickname_format(client, conn, client_entry);
1061   }
1062   client_entry->mode = mode;
1063
1064   if (nick) {
1065     /* Remove the old cache entry and create a new one */
1066     silc_idcache_del_by_context(conn->internal->client_cache, client_entry);
1067     silc_idcache_add(conn->internal->client_cache, nick, client_entry->id,
1068                      client_entry, 0, NULL);
1069   }
1070 }
1071
1072 /* Deletes the client entry and frees all memory. */
1073
1074 void silc_client_del_client_entry(SilcClient client,
1075                                   SilcClientConnection conn,
1076                                   SilcClientEntry client_entry)
1077 {
1078   SILC_LOG_DEBUG(("Start"));
1079
1080   silc_free(client_entry->nickname);
1081   silc_free(client_entry->username);
1082   silc_free(client_entry->realname);
1083   silc_free(client_entry->hostname);
1084   silc_free(client_entry->server);
1085   silc_free(client_entry->id);
1086   silc_free(client_entry->fingerprint);
1087   if (client_entry->public_key)
1088     silc_pkcs_public_key_free(client_entry->public_key);
1089   silc_hash_table_free(client_entry->channels);
1090   if (client_entry->send_key)
1091     silc_cipher_free(client_entry->send_key);
1092   if (client_entry->receive_key)
1093     silc_cipher_free(client_entry->receive_key);
1094   silc_free(client_entry->key);
1095   silc_client_ftp_session_free_client(conn, client_entry);
1096   if (client_entry->ke)
1097     silc_client_abort_key_agreement(client, conn, client_entry);
1098   silc_free(client_entry);
1099 }
1100
1101 /* Removes client from the cache by the client entry. */
1102
1103 SilcBool silc_client_del_client(SilcClient client, SilcClientConnection conn,
1104                             SilcClientEntry client_entry)
1105 {
1106   SilcBool ret = silc_idcache_del_by_context(conn->internal->client_cache,
1107                                          client_entry);
1108
1109   if (ret) {
1110     /* Remove from channels */
1111     silc_client_remove_from_channels(client, conn, client_entry);
1112
1113     /* Free the client entry data */
1114     silc_client_del_client_entry(client, conn, client_entry);
1115   }
1116
1117   return ret;
1118 }
1119
1120 /* Add new channel entry to the ID Cache */
1121
1122 SilcChannelEntry silc_client_add_channel(SilcClient client,
1123                                          SilcClientConnection conn,
1124                                          const char *channel_name,
1125                                          SilcUInt32 mode,
1126                                          SilcChannelID *channel_id)
1127 {
1128   SilcChannelEntry channel;
1129   char *channel_namec;
1130
1131   SILC_LOG_DEBUG(("Start"));
1132
1133   channel = silc_calloc(1, sizeof(*channel));
1134   channel->channel_name = strdup(channel_name);
1135   channel->id = channel_id;
1136   channel->mode = mode;
1137   channel->user_list = silc_hash_table_alloc(1, silc_hash_ptr, NULL, NULL,
1138                                              NULL, NULL, NULL, TRUE);
1139
1140   /* Normalize channel name */
1141   channel_namec = silc_channel_name_check(channel_name, strlen(channel_name),
1142                                           SILC_STRING_UTF8, 256, NULL);
1143   if (!channel_namec) {
1144     silc_free(channel->channel_name);
1145     silc_hash_table_free(channel->user_list);
1146     silc_free(channel);
1147     return NULL;
1148   }
1149
1150   /* Put it to the ID cache */
1151   if (!silc_idcache_add(conn->internal->channel_cache, channel_namec,
1152                         (void *)channel->id, (void *)channel, 0, NULL)) {
1153     silc_free(channel_namec);
1154     silc_free(channel->channel_name);
1155     silc_hash_table_free(channel->user_list);
1156     silc_free(channel);
1157     return NULL;
1158   }
1159
1160   return channel;
1161 }
1162
1163 /* Foreach callbcak to free all users from the channel when deleting a
1164    channel entry. */
1165
1166 static void silc_client_del_channel_foreach(void *key, void *context,
1167                                             void *user_context)
1168 {
1169   SilcChannelUser chu = (SilcChannelUser)context;
1170
1171   SILC_LOG_DEBUG(("Start"));
1172
1173   /* Remove the context from the client's channel hash table as that
1174      table and channel's user_list hash table share this same context. */
1175   silc_hash_table_del(chu->client->channels, chu->channel);
1176   silc_free(chu);
1177 }
1178
1179 /* Removes channel from the cache by the channel entry. */
1180
1181 SilcBool silc_client_del_channel(SilcClient client, SilcClientConnection conn,
1182                              SilcChannelEntry channel)
1183 {
1184   SilcBool ret = silc_idcache_del_by_context(conn->internal->channel_cache,
1185                                          channel);
1186
1187   SILC_LOG_DEBUG(("Start"));
1188
1189   /* Free all client entrys from the users list. The silc_hash_table_free
1190      will free all the entries so they are not freed at the foreach
1191      callback. */
1192   silc_hash_table_foreach(channel->user_list, silc_client_del_channel_foreach,
1193                           NULL);
1194   silc_hash_table_free(channel->user_list);
1195
1196   silc_free(channel->channel_name);
1197   silc_free(channel->topic);
1198   silc_free(channel->id);
1199   if (channel->founder_key)
1200     silc_pkcs_public_key_free(channel->founder_key);
1201   silc_free(channel->key);
1202   if (channel->channel_key)
1203     silc_cipher_free(channel->channel_key);
1204   if (channel->hmac)
1205     silc_hmac_free(channel->hmac);
1206   if (channel->old_channel_keys) {
1207     SilcCipher key;
1208     silc_dlist_start(channel->old_channel_keys);
1209     while ((key = silc_dlist_get(channel->old_channel_keys)) != SILC_LIST_END)
1210       silc_cipher_free(key);
1211     silc_dlist_uninit(channel->old_channel_keys);
1212   }
1213   if (channel->old_hmacs) {
1214     SilcHmac hmac;
1215     silc_dlist_start(channel->old_hmacs);
1216     while ((hmac = silc_dlist_get(channel->old_hmacs)) != SILC_LIST_END)
1217       silc_hmac_free(hmac);
1218     silc_dlist_uninit(channel->old_hmacs);
1219   }
1220   silc_schedule_task_del_by_context(conn->client->schedule, channel);
1221   silc_client_del_channel_private_keys(client, conn, channel);
1222   silc_free(channel);
1223   return ret;
1224 }
1225
1226 /* Replaces the channel ID of the `channel' to `new_id'. Returns FALSE
1227    if the ID could not be changed. */
1228
1229 SilcBool silc_client_replace_channel_id(SilcClient client,
1230                                     SilcClientConnection conn,
1231                                     SilcChannelEntry channel,
1232                                     SilcChannelID *new_id)
1233 {
1234   char *channel_namec;
1235
1236   if (!new_id)
1237     return FALSE;
1238
1239   SILC_LOG_DEBUG(("Old Channel ID id(%s)",
1240                   silc_id_render(channel->id, SILC_ID_CHANNEL)));
1241   SILC_LOG_DEBUG(("New Channel ID id(%s)",
1242                   silc_id_render(new_id, SILC_ID_CHANNEL)));
1243
1244   silc_idcache_del_by_id(conn->internal->channel_cache, channel->id);
1245   silc_free(channel->id);
1246   channel->id = new_id;
1247
1248   /* Normalize channel name */
1249   channel_namec = silc_channel_name_check(channel->channel_name,
1250                                           strlen(channel->channel_name),
1251                                           SILC_STRING_UTF8, 256, NULL);
1252   if (!channel_namec)
1253     return FALSE;
1254
1255   return silc_idcache_add(conn->internal->channel_cache, channel_namec,
1256                           (void *)channel->id, (void *)channel, 0, NULL);
1257
1258 }
1259
1260 /* Finds entry for channel by the channel name. Returns the entry or NULL
1261    if the entry was not found. It is found only if the client is joined
1262    to the channel. */
1263
1264 SilcChannelEntry silc_client_get_channel(SilcClient client,
1265                                          SilcClientConnection conn,
1266                                          char *channel)
1267 {
1268   SilcIDCacheEntry id_cache;
1269   SilcChannelEntry entry;
1270
1271   assert(client && conn);
1272   if (!channel)
1273     return NULL;
1274
1275   SILC_LOG_DEBUG(("Start"));
1276
1277   /* Normalize name for search */
1278   channel = silc_channel_name_check(channel, strlen(channel), SILC_STRING_UTF8,
1279                                     256, NULL);
1280   if (!channel)
1281     return NULL;
1282
1283   if (!silc_idcache_find_by_name_one(conn->internal->channel_cache, channel,
1284                                      &id_cache)) {
1285     silc_free(channel);
1286     return NULL;
1287   }
1288
1289   entry = (SilcChannelEntry)id_cache->context;
1290
1291   SILC_LOG_DEBUG(("Found"));
1292
1293   silc_free(channel);
1294
1295   return entry;
1296 }
1297
1298 /* Finds entry for channel by the channel ID. Returns the entry or NULL
1299    if the entry was not found. It is found only if the client is joined
1300    to the channel. */
1301
1302 SilcChannelEntry silc_client_get_channel_by_id(SilcClient client,
1303                                                SilcClientConnection conn,
1304                                                SilcChannelID *channel_id)
1305 {
1306   SilcIDCacheEntry id_cache;
1307   SilcChannelEntry entry;
1308
1309   assert(client && conn);
1310   if (!channel_id)
1311     return NULL;
1312
1313   SILC_LOG_DEBUG(("Start"));
1314
1315   if (!silc_idcache_find_by_id_one(conn->internal->channel_cache, channel_id,
1316                                    &id_cache))
1317     return NULL;
1318
1319   entry = (SilcChannelEntry)id_cache->context;
1320
1321   SILC_LOG_DEBUG(("Found"));
1322
1323   return entry;
1324 }
1325
1326 typedef struct {
1327   SilcClient client;
1328   SilcClientConnection conn;
1329   union {
1330     SilcChannelID *channel_id;
1331     char *channel_name;
1332   } u;
1333   SilcGetChannelCallback completion;
1334   void *context;
1335 } *GetChannelInternal;
1336
1337 SILC_CLIENT_CMD_FUNC(get_channel_resolve_callback)
1338 {
1339   GetChannelInternal i = (GetChannelInternal)context;
1340   SilcChannelEntry entry;
1341
1342   SILC_LOG_DEBUG(("Start"));
1343
1344   /* Get the channel */
1345   entry = silc_client_get_channel(i->client, i->conn, i->u.channel_name);
1346   if (entry) {
1347     i->completion(i->client, i->conn, &entry, 1, i->context);
1348   } else {
1349     i->completion(i->client, i->conn, NULL, 0, i->context);
1350   }
1351
1352   silc_free(i->u.channel_name);
1353   silc_free(i);
1354 }
1355
1356 /* Resolves channel entry from the server by the channel name. */
1357
1358 void silc_client_get_channel_resolve(SilcClient client,
1359                                      SilcClientConnection conn,
1360                                      char *channel_name,
1361                                      SilcGetChannelCallback completion,
1362                                      void *context)
1363 {
1364   GetChannelInternal i = silc_calloc(1, sizeof(*i));
1365
1366   assert(client && conn && channel_name);
1367
1368   SILC_LOG_DEBUG(("Start"));
1369
1370   i->client = client;
1371   i->conn = conn;
1372   i->u.channel_name = strdup(channel_name);
1373   i->completion = completion;
1374   i->context = context;
1375
1376   /* Register our own command reply for this command */
1377   silc_client_command_register(client, SILC_COMMAND_IDENTIFY, NULL, NULL,
1378                                silc_client_command_reply_identify_i, 0,
1379                                ++conn->cmd_ident);
1380
1381   /* Send the command */
1382   silc_client_command_send(client, conn, SILC_COMMAND_IDENTIFY,
1383                            conn->cmd_ident,
1384                            1, 3, channel_name, strlen(channel_name));
1385
1386   /* Add pending callback */
1387   silc_client_command_pending(conn, SILC_COMMAND_IDENTIFY, conn->cmd_ident,
1388                               silc_client_command_get_channel_resolve_callback,
1389                               (void *)i);
1390 }
1391
1392 SILC_CLIENT_CMD_FUNC(get_channel_by_id_callback)
1393 {
1394   GetChannelInternal i = (GetChannelInternal)context;
1395   SilcChannelEntry entry;
1396
1397   SILC_LOG_DEBUG(("Start"));
1398
1399   /* Get the channel */
1400   entry = silc_client_get_channel_by_id(i->client, i->conn, i->u.channel_id);
1401   if (entry) {
1402     i->completion(i->client, i->conn, &entry, 1, i->context);
1403   } else {
1404     i->completion(i->client, i->conn, NULL, 0, i->context);
1405   }
1406
1407   silc_free(i->u.channel_id);
1408   silc_free(i);
1409 }
1410
1411 /* Resolves channel information from the server by the channel ID. */
1412
1413 void silc_client_get_channel_by_id_resolve(SilcClient client,
1414                                            SilcClientConnection conn,
1415                                            SilcChannelID *channel_id,
1416                                            SilcGetChannelCallback completion,
1417                                            void *context)
1418 {
1419   SilcBuffer idp;
1420   GetChannelInternal i = silc_calloc(1, sizeof(*i));
1421
1422   assert(client && conn && channel_id);
1423
1424   SILC_LOG_DEBUG(("Start"));
1425
1426   i->client = client;
1427   i->conn = conn;
1428   i->u.channel_id = silc_id_dup(channel_id, SILC_ID_CHANNEL);
1429   i->completion = completion;
1430   i->context = context;
1431
1432   /* Register our own command reply for this command */
1433   silc_client_command_register(client, SILC_COMMAND_IDENTIFY, NULL, NULL,
1434                                silc_client_command_reply_identify_i, 0,
1435                                ++conn->cmd_ident);
1436
1437   /* Send the command */
1438   idp = silc_id_payload_encode(channel_id, SILC_ID_CHANNEL);
1439   silc_client_command_send(client, conn, SILC_COMMAND_IDENTIFY,
1440                            conn->cmd_ident,
1441                            1, 5, idp->data, idp->len);
1442   silc_buffer_free(idp);
1443
1444   /* Add pending callback */
1445   silc_client_command_pending(conn, SILC_COMMAND_IDENTIFY, conn->cmd_ident,
1446                               silc_client_command_get_channel_by_id_callback,
1447                               (void *)i);
1448 }
1449
1450 /* Finds entry for server by the server name. */
1451
1452 SilcServerEntry silc_client_get_server(SilcClient client,
1453                                        SilcClientConnection conn,
1454                                        char *server_name)
1455 {
1456   SilcIDCacheEntry id_cache;
1457   SilcServerEntry entry;
1458
1459   assert(client && conn);
1460   if (!server_name)
1461     return NULL;
1462
1463   SILC_LOG_DEBUG(("Start"));
1464
1465   /* Normalize server name for search */
1466   server_name = silc_identifier_check(server_name, strlen(server_name),
1467                                       SILC_STRING_UTF8, 256, NULL);
1468   if (!server_name)
1469     return NULL;
1470
1471   if (!silc_idcache_find_by_name_one(conn->internal->server_cache,
1472                                      server_name, &id_cache)) {
1473     silc_free(server_name);
1474     return NULL;
1475   }
1476
1477   entry = (SilcServerEntry)id_cache->context;
1478
1479   silc_free(server_name);
1480
1481   return entry;
1482 }
1483
1484 /* Finds entry for server by the server ID. */
1485
1486 SilcServerEntry silc_client_get_server_by_id(SilcClient client,
1487                                              SilcClientConnection conn,
1488                                              SilcServerID *server_id)
1489 {
1490   SilcIDCacheEntry id_cache;
1491   SilcServerEntry entry;
1492
1493   assert(client && conn);
1494   if (!server_id)
1495     return NULL;
1496
1497   SILC_LOG_DEBUG(("Start"));
1498
1499   if (!silc_idcache_find_by_id_one(conn->internal->server_cache,
1500                                    (void *)server_id, &id_cache))
1501     return NULL;
1502
1503   entry = (SilcServerEntry)id_cache->context;
1504
1505   return entry;
1506 }
1507
1508 /* Add new server entry */
1509
1510 SilcServerEntry silc_client_add_server(SilcClient client,
1511                                        SilcClientConnection conn,
1512                                        const char *server_name,
1513                                        const char *server_info,
1514                                        SilcServerID *server_id)
1515 {
1516   SilcServerEntry server_entry;
1517   char *server_namec = NULL;
1518
1519   SILC_LOG_DEBUG(("Start"));
1520
1521   server_entry = silc_calloc(1, sizeof(*server_entry));
1522   if (!server_entry || !server_id)
1523     return NULL;
1524
1525   server_entry->server_id = server_id;
1526   if (server_name)
1527     server_entry->server_name = strdup(server_name);
1528   if (server_info)
1529     server_entry->server_info = strdup(server_info);
1530
1531   /* Normalize server name */
1532   if (server_name) {
1533     server_namec = silc_identifier_check(server_name, strlen(server_name),
1534                                          SILC_STRING_UTF8, 256, NULL);
1535     if (!server_namec) {
1536       silc_free(server_entry->server_id);
1537       silc_free(server_entry->server_name);
1538       silc_free(server_entry->server_info);
1539       silc_free(server_entry);
1540       return NULL;
1541     }
1542   }
1543
1544   /* Add server to cache */
1545   if (!silc_idcache_add(conn->internal->server_cache, server_namec,
1546                         server_entry->server_id, server_entry, 0, NULL)) {
1547     silc_free(server_namec);
1548     silc_free(server_entry->server_id);
1549     silc_free(server_entry->server_name);
1550     silc_free(server_entry->server_info);
1551     silc_free(server_entry);
1552     return NULL;
1553   }
1554
1555   return server_entry;
1556 }
1557
1558 /* Removes server from the cache by the server entry. */
1559
1560 SilcBool silc_client_del_server(SilcClient client, SilcClientConnection conn,
1561                             SilcServerEntry server)
1562 {
1563   SilcBool ret = silc_idcache_del_by_context(conn->internal->server_cache, server);
1564   silc_free(server->server_name);
1565   silc_free(server->server_info);
1566   silc_free(server->server_id);
1567   silc_free(server);
1568   return ret;
1569 }
1570
1571 /* Updates the `server_entry' with the new information sent as argument. */
1572
1573 void silc_client_update_server(SilcClient client,
1574                                SilcClientConnection conn,
1575                                SilcServerEntry server_entry,
1576                                const char *server_name,
1577                                const char *server_info)
1578 {
1579   char *server_namec = NULL;
1580
1581   SILC_LOG_DEBUG(("Start"));
1582
1583   if (server_name &&
1584       (!server_entry->server_name ||
1585        !silc_utf8_strcasecmp(server_entry->server_name, server_name))) {
1586
1587     silc_idcache_del_by_context(conn->internal->server_cache, server_entry);
1588     silc_free(server_entry->server_name);
1589     server_entry->server_name = strdup(server_name);
1590
1591     /* Normalize server name */
1592     if (server_name) {
1593       server_namec = silc_identifier_check(server_name, strlen(server_name),
1594                                            SILC_STRING_UTF8, 256, NULL);
1595       if (!server_namec)
1596         return;
1597
1598       silc_idcache_add(conn->internal->server_cache, server_namec,
1599                        server_entry->server_id,
1600                        server_entry, 0, NULL);
1601     }
1602   }
1603
1604   if (server_info &&
1605       (!server_entry->server_info ||
1606        !silc_utf8_strcasecmp(server_entry->server_info, server_info))) {
1607     silc_free(server_entry->server_info);
1608     server_entry->server_info = strdup(server_info);
1609   }
1610 }
1611
1612 /* Formats the nickname of the client specified by the `client_entry'.
1613    If the format is specified by the application this will format the
1614    nickname and replace the old nickname in the client entry. If the
1615    format string is not specified then this function has no effect. */
1616
1617 void silc_client_nickname_format(SilcClient client,
1618                                  SilcClientConnection conn,
1619                                  SilcClientEntry client_entry)
1620 {
1621   char *cp;
1622   char *newnick = NULL;
1623   int i, off = 0, len;
1624   SilcBool freebase;
1625   SilcClientEntry *clients;
1626   SilcUInt32 clients_count = 0;
1627   SilcClientEntry unformatted = NULL;
1628
1629   SILC_LOG_DEBUG(("Start"));
1630
1631   if (!client->internal->params->nickname_format[0])
1632     return;
1633
1634   if (!client_entry->nickname)
1635     return;
1636
1637   /* Get all clients with same nickname. Do not perform the formatting
1638      if there aren't any clients with same nickname unless the application
1639      is forcing us to do so. */
1640   clients = silc_client_get_clients_local(client, conn,
1641                                           client_entry->nickname, NULL,
1642                                           &clients_count);
1643   if (!clients && !client->internal->params->nickname_force_format)
1644     return;
1645
1646   len = 0;
1647   freebase = TRUE;
1648   for (i = 0; i < clients_count; i++) {
1649     if (clients[i]->valid && clients[i] != client_entry)
1650       len++;
1651     if (clients[i]->valid && clients[i] != client_entry &&
1652         silc_utf8_strcasecmp(clients[i]->nickname, client_entry->nickname))
1653       freebase = FALSE;
1654   }
1655   if (!len || freebase)
1656     return;
1657
1658   if (clients_count == 1)
1659     unformatted = clients[0];
1660   else
1661     for (i = 0; i < clients_count; i++)
1662       if (silc_utf8_strncasecmp(clients[i]->nickname, client_entry->nickname,
1663                                 strlen(clients[i]->nickname)))
1664         unformatted = clients[i];
1665
1666   /* If we are changing nickname of our local entry we'll enforce
1667      that we will always get the unformatted nickname.  Give our
1668      format number to the one that is not formatted now. */
1669   if (unformatted && client_entry == conn->local_entry)
1670     client_entry = unformatted;
1671
1672   cp = client->internal->params->nickname_format;
1673   while (*cp) {
1674     if (*cp == '%') {
1675       cp++;
1676       continue;
1677     }
1678
1679     switch(*cp) {
1680     case 'n':
1681       /* Nickname */
1682       if (!client_entry->nickname)
1683         break;
1684       len = strlen(client_entry->nickname);
1685       newnick = silc_realloc(newnick, sizeof(*newnick) * (off + len));
1686       memcpy(&newnick[off], client_entry->nickname, len);
1687       off += len;
1688       break;
1689     case 'h':
1690       /* Stripped hostname */
1691       if (!client_entry->hostname)
1692         break;
1693       len = strcspn(client_entry->hostname, ".");
1694       i = strcspn(client_entry->hostname, "-");
1695       if (i < len)
1696         len = i;
1697       newnick = silc_realloc(newnick, sizeof(*newnick) * (off + len));
1698       memcpy(&newnick[off], client_entry->hostname, len);
1699       off += len;
1700       break;
1701     case 'H':
1702       /* Full hostname */
1703       if (!client_entry->hostname)
1704         break;
1705       len = strlen(client_entry->hostname);
1706       newnick = silc_realloc(newnick, sizeof(*newnick) * (off + len));
1707       memcpy(&newnick[off], client_entry->hostname, len);
1708       off += len;
1709       break;
1710     case 's':
1711       /* Stripped server name */
1712       if (!client_entry->server)
1713         break;
1714       len = strcspn(client_entry->server, ".");
1715       newnick = silc_realloc(newnick, sizeof(*newnick) * (off + len));
1716       memcpy(&newnick[off], client_entry->server, len);
1717       off += len;
1718       break;
1719     case 'S':
1720       /* Full server name */
1721       if (!client_entry->server)
1722         break;
1723       len = strlen(client_entry->server);
1724       newnick = silc_realloc(newnick, sizeof(*newnick) * (off + len));
1725       memcpy(&newnick[off], client_entry->server, len);
1726       off += len;
1727       break;
1728     case 'a':
1729       /* Ascending number */
1730       {
1731         char tmp[6];
1732         int num, max = 1;
1733
1734         if (clients_count == 1)
1735           break;
1736
1737         for (i = 0; i < clients_count; i++) {
1738           if (!silc_utf8_strncasecmp(clients[i]->nickname, newnick, off))
1739             continue;
1740           if (strlen(clients[i]->nickname) <= off)
1741             continue;
1742           num = atoi(&clients[i]->nickname[off]);
1743           if (num > max)
1744             max = num;
1745         }
1746
1747         memset(tmp, 0, sizeof(tmp));
1748         snprintf(tmp, sizeof(tmp) - 1, "%d", ++max);
1749         len = strlen(tmp);
1750         newnick = silc_realloc(newnick, sizeof(*newnick) * (off + len));
1751         memcpy(&newnick[off], tmp, len);
1752         off += len;
1753       }
1754       break;
1755     default:
1756       /* Some other character in the string */
1757       newnick = silc_realloc(newnick, sizeof(*newnick) * (off + 1));
1758       memcpy(&newnick[off], cp, 1);
1759       off++;
1760       break;
1761     }
1762
1763     cp++;
1764   }
1765
1766   newnick = silc_realloc(newnick, sizeof(*newnick) * (off + 1));
1767   newnick[off] = 0;
1768
1769   silc_free(client_entry->nickname);
1770   client_entry->nickname = newnick;
1771   silc_free(clients);
1772 }