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