Added debugs.
[silc.git] / lib / silcclient / client_entry.c
1 /*
2
3   client_entry.c
4
5   Author: Pekka Riikonen <priikone@silcnet.org>
6
7   Copyright (C) 2001 - 2007 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 /************************ Client Searching Locally **************************/
26
27 /* Finds entry for client by the client's ID. Returns the entry or NULL
28    if the entry was not found. */
29
30 SilcClientEntry silc_client_get_client_by_id(SilcClient client,
31                                              SilcClientConnection conn,
32                                              SilcClientID *client_id)
33 {
34   SilcIDCacheEntry id_cache;
35   SilcClientEntry client_entry;
36
37   if (!client || !conn || !client_id)
38     return NULL;
39
40   SILC_LOG_DEBUG(("Finding client by ID (%s)",
41                   silc_id_render(client_id, SILC_ID_CLIENT)));
42
43   silc_mutex_lock(conn->internal->lock);
44
45   /* Find ID from cache */
46   if (!silc_idcache_find_by_id_one(conn->internal->client_cache, client_id,
47                                    &id_cache)) {
48     silc_mutex_unlock(conn->internal->lock);
49     return NULL;
50   }
51
52   client_entry = id_cache->context;
53
54   /* Reference */
55   silc_client_ref_client(client, conn, client_entry);
56   silc_mutex_unlock(conn->internal->lock);
57
58   SILC_LOG_DEBUG(("Found"));
59
60   return client_entry;
61 }
62
63 /* Finds clients by nickname from local cache. */
64
65 SilcDList silc_client_get_clients_local_ext(SilcClient client,
66                                             SilcClientConnection conn,
67                                             const char *nickname,
68                                             SilcBool get_all,
69                                             SilcBool get_valid)
70 {
71   SilcIDCacheEntry id_cache;
72   SilcList list;
73   SilcDList clients;
74   SilcClientEntry entry;
75   char nick[128 + 1], *nicknamec, *parsed = NULL, *format = NULL;
76   char server[256 + 1];
77
78   if (!client || !conn || !nickname)
79     return NULL;
80
81   /* Get nickname from nickname@server string */
82   silc_parse_userfqdn(nickname, nick, sizeof(nick), server, sizeof(server));
83
84   /* Parse nickname in case it is formatted */
85   if (!silc_client_nickname_parse(client, conn, (char *)nick, &parsed))
86     return NULL;
87
88   if (!get_all && parsed)
89     format = (char *)nick;
90   if (!parsed) {
91     parsed = silc_memdup(nick, strlen(nick));
92     if (!parsed)
93       return NULL;
94   }
95
96   SILC_LOG_DEBUG(("Find clients by nickname %s", parsed));
97
98   /* Normalize nickname for search */
99   nicknamec = silc_identifier_check(parsed, strlen(parsed),
100                                     SILC_STRING_UTF8, 128, NULL);
101   if (!nicknamec) {
102     silc_free(parsed);
103     return NULL;
104   }
105
106   clients = silc_dlist_init();
107   if (!clients) {
108     silc_free(nicknamec);
109     silc_free(parsed);
110     return NULL;
111   }
112
113   silc_mutex_lock(conn->internal->lock);
114
115   /* Find from cache */
116   silc_list_init(list, struct SilcIDCacheEntryStruct, next);
117   if (!silc_idcache_find_by_name(conn->internal->client_cache, nicknamec,
118                                  &list)) {
119     silc_mutex_unlock(conn->internal->lock);
120     silc_free(nicknamec);
121     silc_free(parsed);
122     silc_dlist_uninit(clients);
123     return NULL;
124   }
125   silc_list_start(list);
126
127   if (!format && get_all) {
128     /* Take all without any further checking */
129     while ((id_cache = silc_list_get(list))) {
130       entry = id_cache->context;
131       if (!get_valid || entry->internal.valid) {
132         silc_client_ref_client(client, conn, id_cache->context);
133         silc_dlist_add(clients, id_cache->context);
134       }
135     }
136   } else {
137     /* Check multiple cache entries for exact match */
138     while ((id_cache = silc_list_get(list))) {
139       entry = id_cache->context;
140
141       /* If server was provided, find entries that either have no server
142          set or have the same server.  Ignore those that have different
143          server. */
144       if (server[0] && entry->server &&
145           !silc_utf8_strcasecmp(entry->server, server))
146         continue;
147
148       if (silc_utf8_strcasecmp(entry->nickname,
149                                format ? format : parsed) &&
150           (!get_valid || entry->internal.valid)) {
151         silc_client_ref_client(client, conn, entry);
152         silc_dlist_add(clients, entry);
153
154         /* If format is NULL, we find one exact match with the base
155            nickname (parsed). */
156         if (!format)
157           break;
158       }
159     }
160   }
161
162   silc_mutex_unlock(conn->internal->lock);
163
164   silc_free(nicknamec);
165   silc_free(parsed);
166
167   if (!silc_dlist_count(clients)) {
168     silc_dlist_uninit(clients);
169     return NULL;
170   }
171
172   SILC_LOG_DEBUG(("Found %d clients", silc_dlist_count(clients)));
173
174   silc_dlist_start(clients);
175   return clients;
176 }
177
178 /* Finds clients by nickname from local cache. */
179
180 SilcDList silc_client_get_clients_local(SilcClient client,
181                                         SilcClientConnection conn,
182                                         const char *nickname,
183                                         SilcBool return_all)
184 {
185   return silc_client_get_clients_local_ext(client, conn, nickname, return_all,
186                                            TRUE);
187 }
188
189 /********************** Client Resolving from Server ************************/
190
191 /* Resolving context */
192 typedef struct {
193   SilcDList clients;
194   SilcGetClientCallback completion;
195   void *context;
196   SilcClientEntry client_entry;
197 } *SilcClientGetClientInternal;
198
199 /* Resolving command callback */
200
201 static SilcBool silc_client_get_clients_cb(SilcClient client,
202                                            SilcClientConnection conn,
203                                            SilcCommand command,
204                                            SilcStatus status,
205                                            SilcStatus error,
206                                            void *context,
207                                            va_list ap)
208 {
209   SilcClientGetClientInternal i = context;
210   SilcClientEntry client_entry;
211
212   if (error != SILC_STATUS_OK) {
213     SILC_LOG_DEBUG(("Resolving failed: %s", silc_get_status_message(error)));
214
215     if (i->client_entry) {
216       i->client_entry->internal.resolve_cmd_ident = 0;
217       silc_client_unref_client(client, conn, i->client_entry);
218     }
219
220     if (i->completion)
221       i->completion(client, conn, error, NULL, i->context);
222     goto out;
223   }
224
225   /* Add the returned client to list */
226   if (i->completion) {
227     client_entry = va_arg(ap, SilcClientEntry);
228     silc_client_ref_client(client, conn, client_entry);
229     silc_dlist_add(i->clients, client_entry);
230     client_entry->internal.resolve_cmd_ident = 0;
231   }
232
233   if (status == SILC_STATUS_OK || status == SILC_STATUS_LIST_END) {
234     /* Deliver the clients to the caller */
235     if (i->completion) {
236       SILC_LOG_DEBUG(("Resolved %d clients", silc_dlist_count(i->clients)));
237
238       if (i->client_entry) {
239         i->client_entry->internal.resolve_cmd_ident = 0;
240         silc_client_unref_client(client, conn, i->client_entry);
241       }
242
243       silc_dlist_start(i->clients);
244       i->completion(client, conn, SILC_STATUS_OK, i->clients, i->context);
245     }
246     goto out;
247   }
248
249   return TRUE;
250
251  out:
252   silc_client_list_free(client, conn, i->clients);
253   silc_free(i);
254   return FALSE;
255 }
256
257 /* Resolves client information from server by the client ID. */
258
259 SilcUInt16
260 silc_client_get_client_by_id_resolve(SilcClient client,
261                                      SilcClientConnection conn,
262                                      SilcClientID *client_id,
263                                      SilcBuffer attributes,
264                                      SilcGetClientCallback completion,
265                                      void *context)
266 {
267   SilcClientGetClientInternal i;
268   SilcClientEntry client_entry;
269   SilcBuffer idp;
270   SilcUInt16 cmd_ident;
271
272   if (!client || !conn | !client_id) {
273     SILC_LOG_ERROR(("Missing arguments to "
274                     "silc_client_get_clients_by_id_resolve call"));
275     return 0;
276   }
277
278   SILC_LOG_DEBUG(("Resolve client by ID (%s)",
279                   silc_id_render(client_id, SILC_ID_CLIENT)));
280
281   i = silc_calloc(1, sizeof(*i));
282   if (!i)
283     return 0;
284   i->completion = completion;
285   i->context = context;
286   i->clients = silc_dlist_init();
287   if (!i->clients) {
288     silc_free(i);
289     return 0;
290   }
291
292   /* Attach to resolving, if on going */
293   client_entry = silc_client_get_client_by_id(client, conn, client_id);
294   if (client_entry && client_entry->internal.resolve_cmd_ident) {
295     SILC_LOG_DEBUG(("Attach to existing resolving"));
296     silc_client_unref_client(client, conn, client_entry);
297     silc_client_command_pending(conn, SILC_COMMAND_NONE,
298                                 client_entry->internal.resolve_cmd_ident,
299                                 silc_client_get_clients_cb, i);
300     return client_entry->internal.resolve_cmd_ident;
301   }
302
303   /* Send the command */
304   idp = silc_id_payload_encode(client_id, SILC_ID_CLIENT);
305   cmd_ident = silc_client_command_send(client, conn, SILC_COMMAND_WHOIS,
306                                        silc_client_get_clients_cb, i,
307                                        2, 3, silc_buffer_datalen(attributes),
308                                        4, silc_buffer_datalen(idp));
309   if (!cmd_ident && completion)
310     completion(client, conn, SILC_STATUS_ERR_RESOURCE_LIMIT, NULL, context);
311
312   if (client_entry && cmd_ident) {
313     client_entry->internal.resolve_cmd_ident = cmd_ident;
314     i->client_entry = client_entry;
315   } else {
316     silc_client_unref_client(client, conn, client_entry);
317   }
318
319   silc_buffer_free(idp);
320
321   return cmd_ident;
322 }
323
324 /* Finds client entry or entries by the `nickname' and `server'. The
325    completion callback will be called when the client entries has been
326    found.  Used internally by the library. */
327
328 static SilcUInt16 silc_client_get_clients_i(SilcClient client,
329                                             SilcClientConnection conn,
330                                             SilcCommand command,
331                                             const char *nickname,
332                                             const char *server,
333                                             SilcBuffer attributes,
334                                             SilcGetClientCallback completion,
335                                             void *context)
336 {
337   SilcClientGetClientInternal i;
338   char nick[128 + 1], serv[256 + 1], userhost[768 + 1], *parsed = NULL;
339   int len;
340
341   SILC_LOG_DEBUG(("Resolve client by %s command",
342                   silc_get_command_name(command)));
343
344   if (!client || !conn) {
345     SILC_LOG_ERROR(("Missing arguments to silc_client_get_clients call"));
346     return 0;
347   }
348   if (!nickname && !attributes) {
349     SILC_LOG_ERROR(("Missing arguments to silc_client_get_clients call"));
350     return 0;
351   }
352
353   /* Parse server name from the nickname if set */
354   if (silc_parse_userfqdn(nickname, nick, sizeof(nick),
355                           serv, sizeof(serv)) == 2)
356     server = (const char *)serv;
357   nickname = (const char *)nick;
358
359   /* Parse nickname in case it is formatted */
360   if (silc_client_nickname_parse(client, conn, (char *)nickname, &parsed))
361     nickname = (const char *)parsed;
362
363   i = silc_calloc(1, sizeof(*i));
364   if (!i) {
365     silc_free(parsed);
366     return 0;
367   }
368   i->clients = silc_dlist_init();
369   if (!i->clients) {
370     silc_free(parsed);
371     silc_free(i);
372     return 0;
373   }
374   i->completion = completion;
375   i->context = context;
376
377   memset(userhost, 0, sizeof(userhost));
378   if (nickname && server) {
379     len = strlen(nickname) + strlen(server) + 3;
380     silc_strncat(userhost, len, nickname, strlen(nickname));
381     silc_strncat(userhost, len, "@", 1);
382     silc_strncat(userhost, len, server, strlen(server));
383   } else if (nickname) {
384     silc_strncat(userhost, sizeof(userhost) - 1, nickname, strlen(nickname));
385   }
386   silc_free(parsed);
387
388   /* Send the command */
389   if (command == SILC_COMMAND_IDENTIFY)
390     return silc_client_command_send(client, conn, SILC_COMMAND_IDENTIFY,
391                                     silc_client_get_clients_cb, i,
392                                     1, 1, userhost, strlen(userhost));
393   return silc_client_command_send(client, conn, SILC_COMMAND_WHOIS,
394                                   silc_client_get_clients_cb, i,
395                                   2, 1, userhost, strlen(userhost),
396                                   3, silc_buffer_datalen(attributes));
397 }
398
399 /* Get clients from server with IDENTIFY command */
400
401 SilcUInt16 silc_client_get_clients(SilcClient client,
402                                    SilcClientConnection conn,
403                                    const char *nickname,
404                                    const char *server,
405                                    SilcGetClientCallback completion,
406                                    void *context)
407 {
408   return silc_client_get_clients_i(client, conn, SILC_COMMAND_IDENTIFY,
409                                    nickname, server, NULL,
410                                    completion, context);
411 }
412
413 /* Get clients from server with WHOIS command */
414
415 SilcUInt16 silc_client_get_clients_whois(SilcClient client,
416                                          SilcClientConnection conn,
417                                          const char *nickname,
418                                          const char *server,
419                                          SilcBuffer attributes,
420                                          SilcGetClientCallback completion,
421                                          void *context)
422 {
423   return silc_client_get_clients_i(client, conn, SILC_COMMAND_WHOIS,
424                                    nickname, server, attributes,
425                                    completion, context);
426 }
427
428 /* ID list resolving context */
429 typedef struct {
430   SilcGetClientCallback completion;
431   void *context;
432   SilcBuffer client_id_list;
433   SilcUInt32 list_count;
434 } *GetClientsByListInternal;
435
436 static SilcBool silc_client_get_clients_list_cb(SilcClient client,
437                                                 SilcClientConnection conn,
438                                                 SilcCommand command,
439                                                 SilcStatus status,
440                                                 SilcStatus error,
441                                                 void *context,
442                                                 va_list ap)
443 {
444   GetClientsByListInternal i = context;
445   SilcClientEntry client_entry;
446   SilcDList clients;
447   SilcUInt16 idp_len;
448   SilcID id;
449   int c;
450
451   /* Process the list after all replies have been received */
452   if (status != SILC_STATUS_OK && !SILC_STATUS_IS_ERROR(status) &&
453       status != SILC_STATUS_LIST_END)
454     return TRUE;
455
456   SILC_LOG_DEBUG(("Resolved all clients"));
457
458   clients = silc_dlist_init();
459   if (!clients) {
460     status = SILC_STATUS_ERR_RESOURCE_LIMIT;
461     goto out;
462   }
463
464   for (c = 0; c < i->list_count; c++) {
465     /* Get Client ID */
466     SILC_GET16_MSB(idp_len, i->client_id_list->data + 2);
467     idp_len += 4;
468     if (!silc_id_payload_parse_id(i->client_id_list->data, idp_len, &id)) {
469       status = SILC_STATUS_ERR_BAD_CLIENT_ID;
470       goto out;
471     }
472
473     /* Get client entry */
474     client_entry = silc_client_get_client_by_id(client, conn, &id.u.client_id);
475     if (client_entry)
476       silc_dlist_add(clients, client_entry);
477
478     if (!silc_buffer_pull(i->client_id_list, idp_len)) {
479       status = SILC_STATUS_ERR_BAD_CLIENT_ID;
480       goto out;
481     }
482   }
483
484   silc_dlist_start(clients);
485   status = SILC_STATUS_OK;
486   if (i->completion)
487     i->completion(client, conn, status, clients, i->context);
488
489  out:
490   if (status != SILC_STATUS_OK && i->completion)
491     i->completion(client, conn, status, NULL, i->context);
492
493   silc_client_list_free(client, conn, clients);
494   silc_buffer_free(i->client_id_list);
495   silc_free(i);
496
497   return FALSE;
498 }
499
500 /* Gets client entries by the list of client ID's `client_id_list'. This
501    always resolves those client ID's it does not know yet from the server
502    so this function might take a while. The `client_id_list' is a list
503    of ID Payloads added one after other.  JOIN command reply and USERS
504    command reply for example returns this sort of list. The `completion'
505    will be called after the entries are available. */
506
507 SilcUInt16 silc_client_get_clients_by_list(SilcClient client,
508                                            SilcClientConnection conn,
509                                            SilcUInt32 list_count,
510                                            SilcBuffer client_id_list,
511                                            SilcGetClientCallback completion,
512                                            void *context)
513 {
514   GetClientsByListInternal in;
515   SilcClientEntry entry;
516   unsigned char **res_argv = NULL;
517   SilcUInt32 *res_argv_lens = NULL, *res_argv_types = NULL, res_argc = 0;
518   SilcUInt16 idp_len, cmd_ident;
519   SilcID id;
520   int i;
521
522   SILC_LOG_DEBUG(("Resolve clients from Client ID list"));
523
524   if (!client || !conn || !client_id_list)
525     return 0;
526
527   in = silc_calloc(1, sizeof(*in));
528   if (!in)
529     return 0;
530   in->completion = completion;
531   in->context = context;
532   in->list_count = list_count;
533   in->client_id_list = silc_buffer_copy(client_id_list);
534   if (!in->client_id_list)
535     goto err;
536
537   for (i = 0; i < list_count; i++) {
538     /* Get Client ID */
539     SILC_GET16_MSB(idp_len, client_id_list->data + 2);
540     idp_len += 4;
541     if (!silc_id_payload_parse_id(client_id_list->data, idp_len, &id))
542       goto err;
543
544     /* Check if we have this client cached already.  If we don't have the
545        entry or it has incomplete info, then resolve it from the server. */
546     entry = silc_client_get_client_by_id(client, conn, &id.u.client_id);
547     if (!entry || !entry->nickname[0] || !entry->username[0] ||
548         !entry->realname) {
549       if (!res_argv) {
550         res_argv = silc_calloc(list_count, sizeof(*res_argv));
551         res_argv_lens = silc_calloc(list_count, sizeof(*res_argv_lens));
552         res_argv_types = silc_calloc(list_count, sizeof(*res_argv_types));
553         if (!res_argv || !res_argv_lens || !res_argv_types) {
554           silc_client_unref_client(client, conn, entry);
555           goto err;
556         }
557       }
558
559       res_argv[res_argc] = client_id_list->data;
560       res_argv_lens[res_argc] = idp_len;
561       res_argv_types[res_argc] = res_argc + 4;
562       res_argc++;
563     }
564     silc_client_unref_client(client, conn, entry);
565
566     if (!silc_buffer_pull(client_id_list, idp_len))
567       goto err;
568   }
569   silc_buffer_start(client_id_list);
570
571   /* Query the unknown client information from server */
572   if (res_argc) {
573     cmd_ident = silc_client_command_send_argv(client,
574                                               conn, SILC_COMMAND_WHOIS,
575                                               silc_client_get_clients_list_cb,
576                                               in, res_argc, res_argv,
577                                               res_argv_lens,
578                                               res_argv_types);
579     silc_free(res_argv);
580     silc_free(res_argv_lens);
581     silc_free(res_argv_types);
582     return cmd_ident;
583   }
584
585   /* We have the clients in cache, get them and call the completion */
586   silc_client_get_clients_list_cb(client, conn, SILC_COMMAND_WHOIS,
587                                   SILC_STATUS_OK, SILC_STATUS_OK, in, NULL);
588   return 0;
589
590  err:
591   silc_buffer_free(in->client_id_list);
592   silc_free(in);
593   silc_free(res_argv);
594   silc_free(res_argv_lens);
595   silc_free(res_argv_types);
596   return 0;
597 }
598
599 #if 0
600 typedef struct {
601   SilcClient client;
602   SilcClientConnection conn;
603   SilcChannelID channel_id;
604   SilcGetClientCallback completion;
605   void *context;
606   int res_count;
607 } *GetClientsByChannelInternal;
608
609 SILC_CLIENT_CMD_FUNC(get_clients_by_channel_cb)
610 {
611   GetClientsByChannelInternal i = context;
612   SilcClientEntry *clients = NULL;
613   SilcUInt32 clients_count = 0;
614   SilcBool found = FALSE;
615   SilcChannelEntry channel;
616   SilcHashTableList htl;
617   SilcChannelUser chu;
618
619   if (i->res_count) {
620     i->res_count--;
621     if (i->res_count)
622       return;
623   }
624
625   channel = silc_client_get_channel_by_id(i->client, i->conn, &i->channel_id);
626   if (channel && !silc_hash_table_count(channel->user_list)) {
627     clients = silc_calloc(silc_hash_table_count(channel->user_list),
628                           sizeof(*clients));
629     silc_hash_table_list(channel->user_list, &htl);
630     while (silc_hash_table_get(&htl, NULL, (void *)&chu))
631       clients[clients_count++] = chu->client;
632     silc_hash_table_list_reset(&htl);
633     found = TRUE;
634   }
635
636   if (found) {
637     i->completion(i->client, i->conn, clients, clients_count, i->context);
638     silc_free(clients);
639   } else {
640     i->completion(i->client, i->conn, NULL, 0, i->context);
641   }
642
643   silc_free(i);
644 }
645
646 /* Gets client entries by the channel entry indicated by `channel'.  Thus,
647    it resolves the clients currently on that channel. */
648
649 void silc_client_get_clients_by_channel(SilcClient client,
650                                         SilcClientConnection conn,
651                                         SilcChannelEntry channel,
652                                         SilcGetClientCallback completion,
653                                         void *context)
654 {
655   GetClientsByChannelInternal in;
656   SilcHashTableList htl;
657   SilcChannelUser chu;
658   SilcClientEntry entry;
659   unsigned char **res_argv = NULL;
660   SilcUInt32 *res_argv_lens = NULL, *res_argv_types = NULL, res_argc = 0;
661   SilcBuffer idp;
662   SilcBool wait_res = FALSE;
663
664   assert(client && conn && channel);
665
666   SILC_LOG_DEBUG(("Start"));
667
668   in = silc_calloc(1, sizeof(*in));
669   in->client = client;
670   in->conn = conn;
671   in->channel_id = *channel->id;
672   in->completion = completion;
673   in->context = context;
674
675   /* If user list does not exist, send USERS command. */
676   if (!channel->user_list || !silc_hash_table_count(channel->user_list)) {
677     SILC_LOG_DEBUG(("Sending USERS"));
678     silc_client_command_register(client, SILC_COMMAND_USERS, NULL, NULL,
679                                  silc_client_command_reply_users_i, 0,
680                                  ++conn->cmd_ident);
681     silc_client_command_send(client, conn, SILC_COMMAND_USERS,
682                              conn->cmd_ident, 1, 2, channel->channel_name,
683                              strlen(channel->channel_name));
684     silc_client_command_pending(conn, SILC_COMMAND_USERS, conn->cmd_ident,
685                                 silc_client_command_get_clients_by_channel_cb,
686                                 in);
687     return;
688   }
689
690   silc_hash_table_list(channel->user_list, &htl);
691   while (silc_hash_table_get(&htl, NULL, (void *)&chu)) {
692     entry = chu->client;
693
694     /* If the entry has incomplete info, then resolve it from the server. */
695     if (!entry->nickname[0] || !entry->realname) {
696       if (entry->status & SILC_CLIENT_STATUS_RESOLVING) {
697         /* Attach to this resolving and wait until it finishes */
698         silc_client_command_pending(
699                             conn, SILC_COMMAND_NONE,
700                             entry->resolve_cmd_ident,
701                             silc_client_command_get_clients_by_channel_cb,
702                             (void *)in);
703         wait_res = TRUE;
704         in->res_count++;
705         continue;
706       }
707       entry->status |= SILC_CLIENT_STATUS_RESOLVING;
708       entry->resolve_cmd_ident = conn->cmd_ident + 1;
709
710       idp = silc_id_payload_encode(entry->id, SILC_ID_CLIENT);
711
712       /* No we don't have it, query it from the server. Assemble argument
713          table that will be sent for the WHOIS command later. */
714       res_argv = silc_realloc(res_argv, sizeof(*res_argv) *
715                               (res_argc + 1));
716       res_argv_lens = silc_realloc(res_argv_lens, sizeof(*res_argv_lens) *
717                                    (res_argc + 1));
718       res_argv_types = silc_realloc(res_argv_types, sizeof(*res_argv_types) *
719                                     (res_argc + 1));
720       res_argv[res_argc] = silc_memdup(idp->data, idp->len);
721       res_argv_lens[res_argc] = idp->len;
722       res_argv_types[res_argc] = res_argc + 4;
723       res_argc++;
724
725       silc_buffer_free(idp);
726     }
727   }
728   silc_hash_table_list_reset(&htl);
729
730   /* Query the client information from server if the list included clients
731      that we don't know about. */
732   if (res_argc) {
733     SilcBuffer res_cmd;
734
735     /* Send the WHOIS command to server */
736     res_cmd = silc_command_payload_encode(SILC_COMMAND_WHOIS,
737                                           res_argc, res_argv, res_argv_lens,
738                                           res_argv_types, ++conn->cmd_ident);
739     silc_client_packet_send(client, conn->sock, SILC_PACKET_COMMAND,
740                             NULL, 0, NULL, NULL, res_cmd->data, res_cmd->len,
741                             TRUE);
742
743     /* Register our own command reply for this command */
744     silc_client_command_register(client, SILC_COMMAND_WHOIS, NULL, NULL,
745                                  silc_client_command_reply_whois_i, 0,
746                                  conn->cmd_ident);
747
748     /* Process the applications request after reply has been received  */
749     silc_client_command_pending(
750                            conn, SILC_COMMAND_WHOIS, conn->cmd_ident,
751                            silc_client_command_get_clients_by_channel_cb,
752                            (void *)in);
753     in->res_count++;
754
755     silc_buffer_free(res_cmd);
756     silc_free(res_argv);
757     silc_free(res_argv_lens);
758     silc_free(res_argv_types);
759     return;
760   }
761
762   if (wait_res)
763     return;
764
765   /* We have the clients in cache, get them and call the completion */
766   silc_client_command_get_clients_by_channel_cb((void *)in, NULL);
767 }
768 #endif /* 0 */
769
770
771 /************************** Client Entry Routines ***************************/
772
773 /* Creates new client entry and adds it to the ID cache. Returns pointer
774    to the new entry. */
775
776 SilcClientEntry silc_client_add_client(SilcClient client,
777                                        SilcClientConnection conn,
778                                        char *nickname, char *username,
779                                        char *userinfo, SilcClientID *id,
780                                        SilcUInt32 mode)
781 {
782   SilcClientEntry client_entry;
783   char *nick = NULL, parsed[128 + 1];
784
785   SILC_LOG_DEBUG(("Adding new client entry"));
786
787   /* Save the client infos */
788   client_entry = silc_calloc(1, sizeof(*client_entry));
789   if (!client_entry)
790     return NULL;
791
792   silc_rwlock_alloc(&client_entry->internal.lock);
793   silc_atomic_init16(&client_entry->internal.refcnt, 0);
794   client_entry->id = *id;
795   client_entry->mode = mode;
796   client_entry->realname = userinfo ? strdup(userinfo) : NULL;
797
798   silc_parse_userfqdn(nickname, parsed, sizeof(parsed),
799                       client_entry->server, sizeof(client_entry->server));
800   if (nickname && client->internal->params->full_nicknames)
801     silc_snprintf(client_entry->nickname, sizeof(client_entry->nickname),
802                   nickname);
803   else if (nickname)
804     silc_snprintf(client_entry->nickname, sizeof(client_entry->nickname),
805                   parsed);
806
807   silc_parse_userfqdn(username, client_entry->username,
808                       sizeof(client_entry->username),
809                       client_entry->hostname,
810                       sizeof(client_entry->hostname));
811
812   client_entry->channels = silc_hash_table_alloc(NULL, 1, silc_hash_ptr, NULL,
813                                                  NULL, NULL, NULL, NULL, TRUE);
814   if (!client_entry->channels) {
815     silc_free(client_entry->realname);
816     silc_free(client_entry);
817     return NULL;
818   }
819
820   /* Normalize nickname */
821   if (client_entry->nickname[0]) {
822     nick = silc_identifier_check(parsed, strlen(parsed),
823                                  SILC_STRING_UTF8, 128, NULL);
824     if (!nick) {
825       silc_free(client_entry->realname);
826       silc_hash_table_free(client_entry->channels);
827       silc_free(client_entry);
828       return NULL;
829     }
830   }
831
832   silc_mutex_lock(conn->internal->lock);
833
834   /* Add client to cache, the normalized nickname is saved to cache */
835   if (!silc_idcache_add(conn->internal->client_cache, nick,
836                         &client_entry->id, client_entry)) {
837     silc_free(nick);
838     silc_free(client_entry->realname);
839     silc_hash_table_free(client_entry->channels);
840     silc_free(client_entry);
841     silc_mutex_unlock(conn->internal->lock);
842     return NULL;
843   }
844
845   client_entry->nickname_normalized = nick;
846
847   silc_mutex_unlock(conn->internal->lock);
848   silc_client_ref_client(client, conn, client_entry);
849
850   /* Format the nickname */
851   silc_client_nickname_format(client, conn, client_entry, FALSE);
852
853   if (client_entry->nickname[0])
854     client_entry->internal.valid = TRUE;
855
856   SILC_LOG_DEBUG(("Added %p", client_entry));
857
858   return client_entry;
859 }
860
861 /* Updates the `client_entry' with the new information sent as argument.
862    This handles entry locking internally. */
863
864 void silc_client_update_client(SilcClient client,
865                                SilcClientConnection conn,
866                                SilcClientEntry client_entry,
867                                const char *nickname,
868                                const char *username,
869                                const char *userinfo,
870                                SilcUInt32 mode)
871 {
872   char *nick = NULL, parsed[128 + 1];
873
874   SILC_LOG_DEBUG(("Update client entry"));
875
876   silc_rwlock_wrlock(client_entry->internal.lock);
877
878   if (!client_entry->realname && userinfo)
879     client_entry->realname = strdup(userinfo);
880
881   if ((!client_entry->username[0] || !client_entry->hostname[0]) && username)
882     silc_parse_userfqdn(username, client_entry->username,
883                         sizeof(client_entry->username),
884                         client_entry->hostname,
885                         sizeof(client_entry->username));
886
887   if (!client_entry->nickname[0] && nickname) {
888     silc_parse_userfqdn(nickname, parsed, sizeof(parsed),
889                         client_entry->server, sizeof(client_entry->server));
890     if (client->internal->params->full_nicknames)
891       silc_snprintf(client_entry->nickname, sizeof(client_entry->nickname),
892                     nickname);
893     else
894       silc_snprintf(client_entry->nickname, sizeof(client_entry->nickname),
895                     parsed);
896
897     /* Normalize nickname */
898     nick = silc_identifier_check(parsed, strlen(parsed),
899                                  SILC_STRING_UTF8, 128, NULL);
900     if (!nick) {
901       silc_rwlock_unlock(client_entry->internal.lock);
902       return;
903     }
904
905     /* Format nickname */
906     silc_client_nickname_format(client, conn, client_entry,
907                                 client_entry == conn->local_entry);
908
909     /* Update cache entry */
910     silc_mutex_lock(conn->internal->lock);
911     silc_idcache_update_by_context(conn->internal->client_cache,
912                                    client_entry, NULL, nick, TRUE);
913     silc_mutex_unlock(conn->internal->lock);
914     client_entry->nickname_normalized = nick;
915     client_entry->internal.valid = TRUE;
916   }
917   client_entry->mode = mode;
918
919   silc_rwlock_unlock(client_entry->internal.lock);
920 }
921
922 /* Change a client's nickname.  Must be called with `client_entry' locked. */
923
924 SilcBool silc_client_change_nickname(SilcClient client,
925                                      SilcClientConnection conn,
926                                      SilcClientEntry client_entry,
927                                      const char *new_nick,
928                                      SilcClientID *new_id,
929                                      const unsigned char *idp,
930                                      SilcUInt32 idp_len)
931 {
932   char *tmp;
933
934   SILC_LOG_DEBUG(("Change nickname %s to %s", client_entry->nickname,
935                   new_nick));
936
937   /* Normalize nickname */
938   tmp = silc_identifier_check(new_nick, strlen(new_nick),
939                               SILC_STRING_UTF8, 128, NULL);
940   if (!tmp)
941     return FALSE;
942
943   /* Update the client entry */
944   silc_mutex_lock(conn->internal->lock);
945   if (!silc_idcache_update_by_context(conn->internal->client_cache,
946                                       client_entry, new_id, tmp, TRUE)) {
947     silc_free(tmp);
948     silc_mutex_unlock(conn->internal->lock);
949     return FALSE;
950   }
951   silc_mutex_unlock(conn->internal->lock);
952
953   memset(client_entry->nickname, 0, sizeof(client_entry->nickname));
954   memcpy(client_entry->nickname, new_nick, strlen(new_nick));
955   client_entry->nickname_normalized = tmp;
956   silc_client_nickname_format(client, conn, client_entry,
957                               client_entry == conn->local_entry);
958
959   /* For my client entry, update ID and set new ID to packet stream */
960   if (client_entry == conn->local_entry) {
961     if (idp && idp_len) {
962       silc_buffer_enlarge(conn->internal->local_idp, idp_len);
963       silc_buffer_put(conn->internal->local_idp, idp, idp_len);
964     }
965     if (new_id)
966       silc_packet_set_ids(conn->stream, SILC_ID_CLIENT, conn->local_id,
967                           0, NULL);
968   }
969
970   client_entry->internal.valid = TRUE;
971   return TRUE;
972 }
973
974 /* Deletes the client entry and frees all memory. */
975
976 void silc_client_del_client_entry(SilcClient client,
977                                   SilcClientConnection conn,
978                                   SilcClientEntry client_entry)
979 {
980   silc_free(client_entry->realname);
981   silc_free(client_entry->nickname_normalized);
982   silc_free(client_entry->internal.key);
983   if (client_entry->public_key)
984     silc_pkcs_public_key_free(client_entry->public_key);
985   silc_hash_table_free(client_entry->channels);
986   if (client_entry->internal.send_key)
987     silc_cipher_free(client_entry->internal.send_key);
988   if (client_entry->internal.receive_key)
989     silc_cipher_free(client_entry->internal.receive_key);
990   if (client_entry->internal.hmac_send)
991     silc_hmac_free(client_entry->internal.hmac_send);
992   if (client_entry->internal.hmac_receive)
993     silc_hmac_free(client_entry->internal.hmac_receive);
994   silc_client_ftp_session_free_client(client, client_entry);
995   if (client_entry->internal.ke)
996     silc_client_abort_key_agreement(client, conn, client_entry);
997   silc_atomic_uninit16(&client_entry->internal.refcnt);
998   silc_rwlock_free(client_entry->internal.lock);
999   silc_free(client_entry);
1000 }
1001
1002 /* Removes client from the cache by the client entry. */
1003
1004 SilcBool silc_client_del_client(SilcClient client, SilcClientConnection conn,
1005                                 SilcClientEntry client_entry)
1006 {
1007   SilcBool ret;
1008
1009   if (!client_entry)
1010     return FALSE;
1011
1012   if (silc_atomic_sub_int16(&client_entry->internal.refcnt, 1) > 0)
1013     return FALSE;
1014
1015   SILC_LOG_DEBUG(("Deleting client %p", client_entry));
1016
1017   silc_mutex_lock(conn->internal->lock);
1018   ret = silc_idcache_del_by_context(conn->internal->client_cache,
1019                                     client_entry, NULL);
1020   silc_mutex_unlock(conn->internal->lock);
1021
1022   if (ret) {
1023     /* Remove from channels */
1024     silc_client_remove_from_channels(client, conn, client_entry);
1025
1026     /* Free the client entry data */
1027     silc_client_del_client_entry(client, conn, client_entry);
1028   }
1029
1030   return ret;
1031 }
1032
1033 /* Internal routine used to find client by ID and if not found this creates
1034    new client entry and returns it. */
1035
1036 SilcClientEntry silc_client_get_client(SilcClient client,
1037                                        SilcClientConnection conn,
1038                                        SilcClientID *client_id)
1039 {
1040   SilcClientEntry client_entry;
1041
1042   client_entry = silc_client_get_client_by_id(client, conn, client_id);
1043   if (!client_entry) {
1044     client_entry = silc_client_add_client(client, conn, NULL, NULL, NULL,
1045                                           client_id, 0);
1046     if (!client_entry)
1047       return NULL;
1048     silc_client_ref_client(client, conn, client_entry);
1049   }
1050
1051   return client_entry;
1052 }
1053
1054 /* Lock client */
1055
1056 void silc_client_lock_client(SilcClientEntry client_entry)
1057 {
1058   silc_rwlock_rdlock(client_entry->internal.lock);
1059 }
1060
1061 /* Unlock client */
1062
1063 void silc_client_unlock_client(SilcClientEntry client_entry)
1064 {
1065   silc_rwlock_unlock(client_entry->internal.lock);
1066 }
1067
1068 /* Take reference of client entry */
1069
1070 SilcClientEntry silc_client_ref_client(SilcClient client,
1071                                        SilcClientConnection conn,
1072                                        SilcClientEntry client_entry)
1073 {
1074   silc_atomic_add_int16(&client_entry->internal.refcnt, 1);
1075   SILC_LOG_DEBUG(("Client %p refcnt %d->%d", client_entry,
1076                   silc_atomic_get_int16(&client_entry->internal.refcnt) - 1,
1077                   silc_atomic_get_int16(&client_entry->internal.refcnt)));
1078   return client_entry;
1079 }
1080
1081 /* Release reference of client entry */
1082
1083 void silc_client_unref_client(SilcClient client, SilcClientConnection conn,
1084                               SilcClientEntry client_entry)
1085 {
1086   if (client_entry) {
1087     SILC_LOG_DEBUG(("Client %p refcnt %d->%d", client_entry,
1088                     silc_atomic_get_int16(&client_entry->internal.refcnt),
1089                     silc_atomic_get_int16(&client_entry->internal.refcnt) - 1));
1090     silc_client_del_client(client, conn, client_entry);
1091   }
1092 }
1093
1094 /* Free client entry list */
1095
1096 void silc_client_list_free(SilcClient client, SilcClientConnection conn,
1097                            SilcDList client_list)
1098 {
1099   SilcClientEntry client_entry;
1100
1101   if (client_list) {
1102     silc_dlist_start(client_list);
1103     while ((client_entry = silc_dlist_get(client_list)))
1104       silc_client_unref_client(client, conn, client_entry);
1105
1106     silc_dlist_uninit(client_list);
1107   }
1108 }
1109
1110 /* Formats the nickname of the client specified by the `client_entry'.
1111    If the format is specified by the application this will format the
1112    nickname and replace the old nickname in the client entry. If the
1113    format string is not specified then this function has no effect.
1114    Returns the client entry that was formatted. */
1115
1116 SilcClientEntry silc_client_nickname_format(SilcClient client,
1117                                             SilcClientConnection conn,
1118                                             SilcClientEntry client_entry,
1119                                             SilcBool priority)
1120 {
1121   char *cp;
1122   char newnick[128 + 1];
1123   int i, off = 0, len;
1124   SilcDList clients;
1125   SilcClientEntry entry, unformatted = NULL;
1126   SilcBool formatted = FALSE;
1127
1128   if (!client->internal->params->nickname_format[0])
1129     return client_entry;
1130   if (!client_entry->nickname[0])
1131     return NULL;
1132
1133   SILC_LOG_DEBUG(("Format nickname"));
1134
1135   /* Get all clients with same nickname. Do not perform the formatting
1136      if there aren't any clients with same nickname unless the application
1137      is forcing us to do so. */
1138   clients = silc_client_get_clients_local_ext(client, conn,
1139                                               client_entry->nickname,
1140                                               TRUE, FALSE);
1141   if (!clients)
1142     return NULL;
1143   if (silc_dlist_count(clients) == 1 && !priority &&
1144       !client->internal->params->nickname_force_format) {
1145     silc_client_list_free(client, conn, clients);
1146     return client_entry;
1147   }
1148
1149   /* Is the requested client formatted already */
1150   if (client_entry->nickname_normalized &&
1151       !silc_utf8_strcasecmp(client_entry->nickname,
1152                             client_entry->nickname_normalized))
1153     formatted = TRUE;
1154
1155   if (client->internal->params->nickname_force_format)
1156     formatted = FALSE;
1157
1158   /* Find unformatted client entry */
1159   while ((entry = silc_dlist_get(clients))) {
1160     if (!entry->internal.valid)
1161       continue;
1162     if (entry == client_entry)
1163       continue;
1164     if (silc_utf8_strcasecmp(entry->nickname, entry->nickname_normalized)) {
1165       unformatted = entry;
1166       break;
1167     }
1168   }
1169
1170   /* If there are no other unformatted clients and the requested client is
1171      unformatted, just return it. */
1172   if (!unformatted && !formatted) {
1173     silc_client_list_free(client, conn, clients);
1174     return client_entry;
1175   }
1176
1177   /* If priority formatting then the requested client will get the
1178      unformatted nickname, and the unformatted client will get a new
1179      formatted nickname. */
1180   if (priority) {
1181     if (formatted) {
1182       /* Simply change the client's nickname to unformatted */
1183       if (!silc_client_nickname_parse(client, conn, client_entry->nickname,
1184                                       &cp))
1185         return NULL;
1186
1187       silc_snprintf(client_entry->nickname, sizeof(client_entry->nickname),
1188                     cp);
1189       silc_free(cp);
1190     }
1191
1192     if (!unformatted) {
1193       /* There was no other unformatted client */
1194       silc_client_list_free(client, conn, clients);
1195       return client_entry;
1196     }
1197
1198     /* Now format the previously unformatted client */
1199     client_entry = unformatted;
1200     formatted = FALSE;
1201   }
1202
1203   /* If already formatted just return it */
1204   if (formatted) {
1205     silc_client_list_free(client, conn, clients);
1206     return client_entry;
1207   }
1208
1209   memset(newnick, 0, sizeof(newnick));
1210   cp = client->internal->params->nickname_format;
1211   while (cp && *cp) {
1212     if (*cp == '%') {
1213       cp++;
1214       continue;
1215     }
1216
1217     switch(*cp) {
1218     case 'n':
1219       /* Nickname */
1220       if (!client_entry->nickname[0])
1221         break;
1222       len = strlen(client_entry->nickname);
1223       memcpy(&newnick[off], client_entry->nickname, len);
1224       off += len;
1225       break;
1226     case 'h':
1227       /* Stripped hostname */
1228       if (!client_entry->hostname[0])
1229         break;
1230       len = strcspn(client_entry->hostname, ".");
1231       i = strcspn(client_entry->hostname, "-");
1232       if (i < len)
1233         len = i;
1234       memcpy(&newnick[off], client_entry->hostname, len);
1235       off += len;
1236       break;
1237     case 'H':
1238       /* Full hostname */
1239       if (!client_entry->hostname[0])
1240         break;
1241       len = strlen(client_entry->hostname);
1242       memcpy(&newnick[off], client_entry->hostname, len);
1243       off += len;
1244       break;
1245     case 'a':
1246       /* Ascending number */
1247       {
1248         char tmp[6];
1249         int num, max = 1;
1250
1251         if (silc_dlist_count(clients) == 1)
1252           break;
1253
1254         silc_dlist_start(clients);
1255         while ((entry = silc_dlist_get(clients))) {
1256           if (!silc_utf8_strncasecmp(entry->nickname, newnick, off))
1257             continue;
1258           if (strlen(entry->nickname) <= off)
1259             continue;
1260           num = atoi(&entry->nickname[off]);
1261           if (num > max)
1262             max = num;
1263         }
1264
1265         memset(tmp, 0, sizeof(tmp));
1266         silc_snprintf(tmp, sizeof(tmp) - 1, "%d", ++max);
1267         len = strlen(tmp);
1268         memcpy(&newnick[off], tmp, len);
1269         off += len;
1270       }
1271       break;
1272     default:
1273       /* Some other character in the string */
1274       memcpy(&newnick[off], cp, 1);
1275       off++;
1276       break;
1277     }
1278
1279     cp++;
1280   }
1281
1282   newnick[off] = 0;
1283   memcpy(client_entry->nickname, newnick, strlen(newnick));
1284   silc_client_list_free(client, conn, clients);
1285
1286   return client_entry;
1287 }
1288
1289 /* Parses nickname according to nickname format string */
1290
1291 SilcBool silc_client_nickname_parse(SilcClient client,
1292                                     SilcClientConnection conn,
1293                                     char *nickname,
1294                                     char **ret_nick)
1295 {
1296   char *cp, s = 0, e = 0, *nick;
1297   SilcBool n = FALSE;
1298   int len;
1299
1300   if (!client->internal->params->nickname_format[0]) {
1301     *ret_nick = NULL;
1302     return TRUE;
1303   }
1304
1305   if (!nickname || !nickname[0])
1306     return FALSE;
1307
1308   cp = client->internal->params->nickname_format;
1309   while (cp && *cp) {
1310     if (*cp == '%') {
1311       cp++;
1312       continue;
1313     }
1314
1315     switch(*cp) {
1316     case 'n':
1317       n = TRUE;
1318       break;
1319
1320     case 'h':
1321     case 'H':
1322     case 'a':
1323       break;
1324
1325     default:
1326       /* Get separator character */
1327       if (n)
1328         e = *cp;
1329       else
1330         s = *cp;
1331       break;
1332     }
1333
1334     cp++;
1335   }
1336   if (!n)
1337     return FALSE;
1338
1339   /* Parse the nickname */
1340   nick = nickname;
1341   len = strlen(nick);
1342   if (s)
1343     if (strchr(nickname, s))
1344       nick = strchr(nickname, s) + 1;
1345   if (e)
1346     if (strchr(nick, e))
1347       len = strchr(nick, e) - nick;
1348   if (!len)
1349     return FALSE;
1350
1351   *ret_nick = silc_memdup(nick, len);
1352   if (!(*ret_nick))
1353     return FALSE;
1354
1355   SILC_LOG_DEBUG(("Parsed nickname: %s", *ret_nick));
1356
1357   return TRUE;
1358 }
1359
1360 /************************ Channel Searching Locally *************************/
1361
1362 /* Finds entry for channel by the channel name. Returns the entry or NULL
1363    if the entry was not found. It is found only if the client is joined
1364    to the channel. */
1365
1366 SilcChannelEntry silc_client_get_channel(SilcClient client,
1367                                          SilcClientConnection conn,
1368                                          char *channel)
1369 {
1370   SilcList list;
1371   SilcIDCacheEntry id_cache;
1372   SilcChannelEntry entry = NULL;
1373   char chname[256 + 1], server[256 + 1];
1374
1375   if (!client || !conn || !channel)
1376     return NULL;
1377
1378   SILC_LOG_DEBUG(("Find channel %s", channel));
1379
1380   /* Parse server name from channel name */
1381   silc_parse_userfqdn(channel, chname, sizeof(chname), server, sizeof(server));
1382
1383   /* Normalize name for search */
1384   channel = silc_channel_name_check(chname, strlen(chname), SILC_STRING_UTF8,
1385                                     256, NULL);
1386   if (!channel)
1387     return NULL;
1388
1389   silc_mutex_lock(conn->internal->lock);
1390
1391   if (!silc_idcache_find_by_name(conn->internal->channel_cache, channel,
1392                                  &list)) {
1393     silc_mutex_unlock(conn->internal->lock);
1394     silc_free(channel);
1395     return NULL;
1396   }
1397
1398   /* If server name was specified with channel name, find the correct
1399      channel entry with the server name.  There can only be one channel
1400      with same name on same server. */
1401   silc_list_start(list);
1402   if (server[0]) {
1403     while ((id_cache = silc_list_get(list))) {
1404       entry = id_cache->context;
1405       if (!entry->server[0])
1406         continue;
1407       if (silc_utf8_strcasecmp(entry->server, server))
1408         break;
1409     }
1410   } else {
1411     /* Get first channel without server name specified or one with our
1412        current server connection name */
1413     while ((id_cache = silc_list_get(list))) {
1414       entry = id_cache->context;
1415       if (!entry->server[0])
1416         break;
1417       if (silc_utf8_strcasecmp(entry->server, conn->remote_host))
1418         break;
1419     }
1420   }
1421
1422   if (!id_cache) {
1423     silc_mutex_unlock(conn->internal->lock);
1424     silc_free(channel);
1425     return NULL;
1426   }
1427
1428   SILC_LOG_DEBUG(("Found channel %s%s%s", entry->channel_name,
1429                   entry->server[0] ? "@" : "", entry->server));
1430
1431   /* Reference */
1432   silc_client_ref_channel(client, conn, entry);
1433   silc_mutex_unlock(conn->internal->lock);
1434
1435   silc_free(channel);
1436
1437   return entry;
1438 }
1439
1440 /* Finds entry for channel by the channel ID. Returns the entry or NULL
1441    if the entry was not found. It is found only if the client is joined
1442    to the channel. */
1443
1444 SilcChannelEntry silc_client_get_channel_by_id(SilcClient client,
1445                                                SilcClientConnection conn,
1446                                                SilcChannelID *channel_id)
1447 {
1448   SilcIDCacheEntry id_cache;
1449   SilcChannelEntry entry;
1450
1451   if (!client || !conn || !channel_id)
1452     return NULL;
1453
1454   SILC_LOG_DEBUG(("Find channel by id %s",
1455                   silc_id_render(channel_id, SILC_ID_CHANNEL)));
1456
1457   silc_mutex_lock(conn->internal->lock);
1458
1459   if (!silc_idcache_find_by_id_one(conn->internal->channel_cache, channel_id,
1460                                    &id_cache)) {
1461     silc_mutex_unlock(conn->internal->lock);
1462     return NULL;
1463   }
1464
1465   SILC_LOG_DEBUG(("Found"));
1466
1467   entry = id_cache->context;
1468
1469   /* Reference */
1470   silc_client_ref_channel(client, conn, entry);
1471   silc_mutex_unlock(conn->internal->lock);
1472
1473   return entry;
1474 }
1475
1476 /********************** Channel Resolving from Server ***********************/
1477
1478 /* Channel resolving context */
1479 typedef struct {
1480   SilcDList channels;
1481   SilcGetChannelCallback completion;
1482   void *context;
1483 } *SilcClientGetChannelInternal;
1484
1485 /* Resolving command callback */
1486
1487 static SilcBool silc_client_get_channel_cb(SilcClient client,
1488                                            SilcClientConnection conn,
1489                                            SilcCommand command,
1490                                            SilcStatus status,
1491                                            SilcStatus error,
1492                                            void *context,
1493                                            va_list ap)
1494 {
1495   SilcClientGetChannelInternal i = context;
1496   SilcChannelEntry entry;
1497
1498   if (error != SILC_STATUS_OK) {
1499     SILC_LOG_DEBUG(("Resolving failed: %s", silc_get_status_message(error)));
1500     if (i->completion)
1501       i->completion(client, conn, error, NULL, i->context);
1502     goto out;
1503   }
1504
1505   /* Add the returned channel to list */
1506   if (i->completion) {
1507     entry = va_arg(ap, SilcChannelEntry);
1508     silc_client_ref_channel(client, conn, entry);
1509     silc_dlist_add(i->channels, entry);
1510   }
1511
1512   if (status == SILC_STATUS_OK || status == SILC_STATUS_LIST_END) {
1513     /* Deliver the channels to the caller */
1514     if (i->completion) {
1515       SILC_LOG_DEBUG(("Resolved %d channels", silc_dlist_count(i->channels)));
1516       silc_dlist_start(i->channels);
1517       i->completion(client, conn, SILC_STATUS_OK, i->channels, i->context);
1518     }
1519     goto out;
1520   }
1521
1522   return TRUE;
1523
1524  out:
1525   silc_client_list_free_channels(client, conn, i->channels);
1526   silc_free(i);
1527   return FALSE;
1528 }
1529
1530 /* Resolves channel entry from the server by the channel name. */
1531
1532 void silc_client_get_channel_resolve(SilcClient client,
1533                                      SilcClientConnection conn,
1534                                      char *channel_name,
1535                                      SilcGetChannelCallback completion,
1536                                      void *context)
1537 {
1538   SilcClientGetChannelInternal i;
1539
1540   if (!client || !conn || !channel_name || !completion)
1541     return;
1542
1543   SILC_LOG_DEBUG(("Resolve channel %s", channel_name));
1544
1545   i = silc_calloc(1, sizeof(*i));
1546   if (!i)
1547     return;
1548   i->completion = completion;
1549   i->context = context;
1550   i->channels = silc_dlist_init();
1551   if (!i->channels) {
1552     silc_free(i);
1553     return;
1554   }
1555
1556   /* Send the command */
1557   if (!silc_client_command_send(client, conn, SILC_COMMAND_IDENTIFY,
1558                                 silc_client_get_channel_cb, i, 1,
1559                                 3, channel_name, strlen(channel_name))) {
1560     if (completion)
1561       completion(client, conn, SILC_STATUS_ERR_RESOURCE_LIMIT, NULL, context);
1562   }
1563 }
1564
1565 /* Resolves channel information from the server by the channel ID. */
1566
1567 SilcUInt16
1568 silc_client_get_channel_by_id_resolve(SilcClient client,
1569                                       SilcClientConnection conn,
1570                                       SilcChannelID *channel_id,
1571                                       SilcGetChannelCallback completion,
1572                                       void *context)
1573 {
1574   SilcClientGetChannelInternal i;
1575   SilcBuffer idp;
1576   SilcUInt16 cmd_ident;
1577
1578   if (!client || !conn || !channel_id || !completion)
1579     return 0;
1580
1581   SILC_LOG_DEBUG(("Resolve channel by id %s",
1582                   silc_id_render(channel_id, SILC_ID_CHANNEL)));
1583
1584   i = silc_calloc(1, sizeof(*i));
1585   if (!i)
1586     return 0;
1587   i->completion = completion;
1588   i->context = context;
1589   i->channels = silc_dlist_init();
1590   if (!i->channels) {
1591     silc_free(i);
1592     return 0;
1593   }
1594
1595   /* Send the command */
1596   idp = silc_id_payload_encode(channel_id, SILC_ID_CHANNEL);
1597   cmd_ident = silc_client_command_send(client, conn, SILC_COMMAND_IDENTIFY,
1598                                        silc_client_get_channel_cb, i, 1,
1599                                        5, silc_buffer_datalen(idp));
1600   silc_buffer_free(idp);
1601   if (!cmd_ident && completion)
1602     completion(client, conn, SILC_STATUS_ERR_RESOURCE_LIMIT, NULL, context);
1603
1604   return cmd_ident;
1605 }
1606
1607 /************************* Channel Entry Routines ***************************/
1608
1609 /* Add new channel entry to the ID Cache */
1610
1611 SilcChannelEntry silc_client_add_channel(SilcClient client,
1612                                          SilcClientConnection conn,
1613                                          const char *channel_name,
1614                                          SilcUInt32 mode,
1615                                          SilcChannelID *channel_id)
1616 {
1617   SilcChannelEntry channel;
1618   char *channel_namec, name[256 + 1];
1619
1620   SILC_LOG_DEBUG(("Adding channel %s", channel_name));
1621
1622   channel = silc_calloc(1, sizeof(*channel));
1623   if (!channel)
1624     return NULL;
1625
1626   silc_rwlock_alloc(&channel->internal.lock);
1627   silc_atomic_init16(&channel->internal.refcnt, 0);
1628   channel->id = *channel_id;
1629   channel->mode = mode;
1630
1631   silc_parse_userfqdn(channel_name, name, sizeof(name),
1632                       channel->server, sizeof(channel->server));
1633   if (client->internal->params->full_channel_names)
1634     channel->channel_name = strdup(channel_name);
1635   else
1636     channel->channel_name = strdup(name);
1637
1638   if (!channel->channel_name) {
1639     silc_rwlock_free(channel->internal.lock);
1640     silc_atomic_uninit16(&channel->internal.refcnt);
1641     silc_free(channel);
1642     return NULL;
1643   }
1644
1645   channel->user_list = silc_hash_table_alloc(NULL, 1, silc_hash_ptr, NULL, NULL,
1646                                              NULL, NULL, NULL, TRUE);
1647   if (!channel->user_list) {
1648     silc_rwlock_free(channel->internal.lock);
1649     silc_atomic_uninit16(&channel->internal.refcnt);
1650     silc_free(channel->channel_name);
1651     silc_free(channel);
1652     return NULL;
1653   }
1654
1655   /* Normalize channel name */
1656   channel_namec = silc_channel_name_check(name, strlen(name),
1657                                           SILC_STRING_UTF8, 256, NULL);
1658   if (!channel_namec) {
1659     silc_rwlock_free(channel->internal.lock);
1660     silc_atomic_uninit16(&channel->internal.refcnt);
1661     silc_free(channel->channel_name);
1662     silc_hash_table_free(channel->user_list);
1663     silc_free(channel);
1664     return NULL;
1665   }
1666
1667   silc_mutex_lock(conn->internal->lock);
1668
1669   /* Add channel to cache, the normalized channel name is saved to cache */
1670   if (!silc_idcache_add(conn->internal->channel_cache, channel_namec,
1671                         &channel->id, channel)) {
1672     silc_rwlock_free(channel->internal.lock);
1673     silc_atomic_uninit16(&channel->internal.refcnt);
1674     silc_free(channel_namec);
1675     silc_free(channel->channel_name);
1676     silc_hash_table_free(channel->user_list);
1677     silc_free(channel);
1678     silc_mutex_unlock(conn->internal->lock);
1679     return NULL;
1680   }
1681
1682   silc_mutex_unlock(conn->internal->lock);
1683   silc_client_ref_channel(client, conn, channel);
1684
1685   SILC_LOG_DEBUG(("Added %p", channel));
1686
1687   return channel;
1688 }
1689
1690 /* Removes channel from the cache by the channel entry. */
1691
1692 SilcBool silc_client_del_channel(SilcClient client, SilcClientConnection conn,
1693                                  SilcChannelEntry channel)
1694 {
1695   SilcIDCacheEntry id_cache;
1696   SilcBool ret = TRUE;
1697   SilcCipher key;
1698   SilcHmac hmac;
1699   char *namec;
1700
1701   if (!channel)
1702     return FALSE;
1703
1704   if (silc_atomic_sub_int16(&channel->internal.refcnt, 1) > 0)
1705     return FALSE;
1706
1707   SILC_LOG_DEBUG(("Deleting channel %p", channel));
1708
1709   silc_mutex_lock(conn->internal->lock);
1710   if (silc_idcache_find_by_context(conn->internal->channel_cache, channel,
1711                                    &id_cache)) {
1712     namec = id_cache->name;
1713     ret = silc_idcache_del_by_context(conn->internal->channel_cache,
1714                                       channel, NULL);
1715     silc_free(namec);
1716   }
1717   silc_mutex_unlock(conn->internal->lock);
1718
1719   if (!ret)
1720     return FALSE;
1721
1722   silc_client_empty_channel(client, conn, channel);
1723   silc_client_del_channel_private_keys(client, conn, channel);
1724   silc_hash_table_free(channel->user_list);
1725   silc_free(channel->channel_name);
1726   silc_free(channel->topic);
1727   if (channel->founder_key)
1728     silc_pkcs_public_key_free(channel->founder_key);
1729   if (channel->internal.send_key)
1730     silc_cipher_free(channel->internal.send_key);
1731   if (channel->internal.receive_key)
1732     silc_cipher_free(channel->internal.receive_key);
1733   if (channel->internal.hmac)
1734     silc_hmac_free(channel->internal.hmac);
1735   if (channel->internal.old_channel_keys) {
1736     silc_dlist_start(channel->internal.old_channel_keys);
1737     while ((key = silc_dlist_get(channel->internal.old_channel_keys)))
1738       silc_cipher_free(key);
1739     silc_dlist_uninit(channel->internal.old_channel_keys);
1740   }
1741   if (channel->internal.old_hmacs) {
1742     silc_dlist_start(channel->internal.old_hmacs);
1743     while ((hmac = silc_dlist_get(channel->internal.old_hmacs)))
1744       silc_hmac_free(hmac);
1745     silc_dlist_uninit(channel->internal.old_hmacs);
1746   }
1747   if (channel->channel_pubkeys)
1748     silc_argument_list_free(channel->channel_pubkeys,
1749                             SILC_ARGUMENT_PUBLIC_KEY);
1750   silc_atomic_uninit16(&channel->internal.refcnt);
1751   silc_rwlock_free(channel->internal.lock);
1752   silc_schedule_task_del_by_context(conn->client->schedule, channel);
1753   silc_free(channel);
1754
1755   return ret;
1756 }
1757
1758 /* Replaces the channel ID of the `channel' to `new_id'. Returns FALSE
1759    if the ID could not be changed.  This handles entry locking internally. */
1760
1761 SilcBool silc_client_replace_channel_id(SilcClient client,
1762                                         SilcClientConnection conn,
1763                                         SilcChannelEntry channel,
1764                                         SilcChannelID *new_id)
1765 {
1766   SilcBool ret = FALSE;
1767
1768   if (!new_id)
1769     return FALSE;
1770
1771   SILC_LOG_DEBUG(("Old Channel ID id(%s)",
1772                   silc_id_render(&channel->id, SILC_ID_CHANNEL)));
1773   SILC_LOG_DEBUG(("New Channel ID id(%s)",
1774                   silc_id_render(new_id, SILC_ID_CHANNEL)));
1775
1776   /* Update the ID */
1777   silc_rwlock_wrlock(channel->internal.lock);
1778   silc_mutex_lock(conn->internal->lock);
1779   silc_idcache_update_by_context(conn->internal->channel_cache, channel,
1780                                  new_id, NULL, FALSE);
1781   silc_mutex_unlock(conn->internal->lock);
1782   silc_rwlock_unlock(channel->internal.lock);
1783
1784   return ret;
1785 }
1786
1787 /* Lock channel */
1788
1789 void silc_client_lock_channel(SilcChannelEntry channel_entry)
1790 {
1791   silc_rwlock_rdlock(channel_entry->internal.lock);
1792 }
1793
1794 /* Unlock channel */
1795
1796 void silc_client_unlock_channel(SilcChannelEntry channel_entry)
1797 {
1798   silc_rwlock_unlock(channel_entry->internal.lock);
1799 }
1800
1801 /* Take reference of channel entry */
1802
1803 SilcChannelEntry silc_client_ref_channel(SilcClient client,
1804                                          SilcClientConnection conn,
1805                                          SilcChannelEntry channel_entry)
1806 {
1807   silc_atomic_add_int16(&channel_entry->internal.refcnt, 1);
1808   SILC_LOG_DEBUG(("Channel %p refcnt %d->%d", channel_entry,
1809                   silc_atomic_get_int16(&channel_entry->internal.refcnt) - 1,
1810                   silc_atomic_get_int16(&channel_entry->internal.refcnt)));
1811   return channel_entry;
1812 }
1813
1814 /* Release reference of channel entry */
1815
1816 void silc_client_unref_channel(SilcClient client, SilcClientConnection conn,
1817                                SilcChannelEntry channel_entry)
1818 {
1819   if (channel_entry) {
1820     SILC_LOG_DEBUG(("Channel %p refcnt %d->%d", channel_entry,
1821                     silc_atomic_get_int16(&channel_entry->internal.refcnt),
1822                     silc_atomic_get_int16(&channel_entry->internal.refcnt)
1823                     - 1));
1824     silc_client_del_channel(client, conn, channel_entry);
1825   }
1826 }
1827
1828 /* Free channel entry list */
1829
1830 void silc_client_list_free_channels(SilcClient client,
1831                                     SilcClientConnection conn,
1832                                     SilcDList channel_list)
1833 {
1834   SilcChannelEntry channel_entry;
1835
1836   if (channel_list) {
1837     silc_dlist_start(channel_list);
1838     while ((channel_entry = silc_dlist_get(channel_list)))
1839       silc_client_unref_channel(client, conn, channel_entry);
1840
1841     silc_dlist_uninit(channel_list);
1842   }
1843 }
1844
1845 /************************* Server Searching Locally *************************/
1846
1847 /* Finds entry for server by the server name. */
1848
1849 SilcServerEntry silc_client_get_server(SilcClient client,
1850                                        SilcClientConnection conn,
1851                                        char *server_name)
1852 {
1853   SilcIDCacheEntry id_cache;
1854   SilcServerEntry entry;
1855
1856   if (!client || !conn || !server_name)
1857     return NULL;
1858
1859   SILC_LOG_DEBUG(("Find server by name %s", server_name));
1860
1861   /* Normalize server name for search */
1862   server_name = silc_identifier_check(server_name, strlen(server_name),
1863                                       SILC_STRING_UTF8, 256, NULL);
1864   if (!server_name)
1865     return NULL;
1866
1867   silc_mutex_lock(conn->internal->lock);
1868
1869   if (!silc_idcache_find_by_name_one(conn->internal->server_cache,
1870                                      server_name, &id_cache)) {
1871     silc_free(server_name);
1872     silc_mutex_unlock(conn->internal->lock);
1873     return NULL;
1874   }
1875
1876   SILC_LOG_DEBUG(("Found"));
1877
1878   /* Reference */
1879   entry = id_cache->context;
1880   silc_client_ref_server(client, conn, entry);
1881
1882   silc_mutex_unlock(conn->internal->lock);
1883
1884   silc_free(server_name);
1885
1886   return entry;
1887 }
1888
1889 /* Finds entry for server by the server ID. */
1890
1891 SilcServerEntry silc_client_get_server_by_id(SilcClient client,
1892                                              SilcClientConnection conn,
1893                                              SilcServerID *server_id)
1894 {
1895   SilcIDCacheEntry id_cache;
1896   SilcServerEntry entry;
1897
1898   if (!client || !conn || !server_id)
1899     return NULL;
1900
1901   SILC_LOG_DEBUG(("Find server by id %s",
1902                   silc_id_render(server_id, SILC_ID_SERVER)));
1903
1904   silc_mutex_lock(conn->internal->lock);
1905
1906   if (!silc_idcache_find_by_id_one(conn->internal->server_cache,
1907                                    server_id, &id_cache)) {
1908     silc_mutex_unlock(conn->internal->lock);
1909     return NULL;
1910   }
1911
1912   SILC_LOG_DEBUG(("Found"));
1913
1914   /* Reference */
1915   entry = id_cache->context;
1916   silc_client_ref_server(client, conn, entry);
1917
1918   silc_mutex_unlock(conn->internal->lock);
1919
1920   return entry;
1921 }
1922
1923 /*********************** Server Resolving from Server ***********************/
1924
1925 /* Resolving context */
1926 typedef struct {
1927   SilcDList servers;
1928   SilcGetServerCallback completion;
1929   void *context;
1930 } *SilcClientGetServerInternal;
1931
1932 /* Resolving command callback */
1933
1934 static SilcBool silc_client_get_server_cb(SilcClient client,
1935                                           SilcClientConnection conn,
1936                                           SilcCommand command,
1937                                           SilcStatus status,
1938                                           SilcStatus error,
1939                                           void *context,
1940                                           va_list ap)
1941 {
1942   SilcClientGetServerInternal i = context;
1943   SilcServerEntry server;
1944
1945   if (error != SILC_STATUS_OK) {
1946     SILC_LOG_DEBUG(("Resolving failed: %s", silc_get_status_message(error)));
1947     if (i->completion)
1948       i->completion(client, conn, error, NULL, i->context);
1949     goto out;
1950   }
1951
1952   /* Add the returned servers to list */
1953   if (i->completion) {
1954     server = va_arg(ap, SilcServerEntry);
1955     silc_client_ref_server(client, conn, server);
1956     silc_dlist_add(i->servers, server);
1957     server->internal.resolve_cmd_ident = 0;
1958   }
1959
1960   if (status == SILC_STATUS_OK || status == SILC_STATUS_LIST_END) {
1961     /* Deliver the servers to the caller */
1962     if (i->completion) {
1963       SILC_LOG_DEBUG(("Resolved %d servers", silc_dlist_count(i->servers)));
1964       silc_dlist_start(i->servers);
1965       i->completion(client, conn, SILC_STATUS_OK, i->servers, i->context);
1966     }
1967     goto out;
1968   }
1969
1970   return TRUE;
1971
1972  out:
1973   silc_client_list_free_servers(client, conn, i->servers);
1974   silc_free(i);
1975   return FALSE;
1976 }
1977
1978 /* Resolve server by server ID */
1979
1980 SilcUInt16
1981 silc_client_get_server_by_id_resolve(SilcClient client,
1982                                      SilcClientConnection conn,
1983                                      SilcServerID *server_id,
1984                                      SilcGetServerCallback completion,
1985                                      void *context)
1986 {
1987   SilcClientGetServerInternal i;
1988   SilcServerEntry server;
1989   SilcBuffer idp;
1990   SilcUInt16 cmd_ident;
1991
1992   if (!client || !conn || !server_id || !completion)
1993     return 0;
1994
1995   SILC_LOG_DEBUG(("Resolve server by id %s",
1996                   silc_id_render(server_id, SILC_ID_SERVER)));
1997
1998   i = silc_calloc(1, sizeof(*i));
1999   if (!i)
2000     return 0;
2001   i->completion = completion;
2002   i->context = context;
2003   i->servers = silc_dlist_init();
2004   if (!i->servers) {
2005     silc_free(i);
2006     return 0;
2007   }
2008
2009   /* Attach to resolving, if on going */
2010   server = silc_client_get_server_by_id(client, conn, server_id);
2011   if (server && server->internal.resolve_cmd_ident) {
2012     SILC_LOG_DEBUG(("Attach to existing resolving"));
2013     silc_client_unref_server(client, conn, server);
2014     silc_client_command_pending(conn, SILC_COMMAND_NONE,
2015                                 server->internal.resolve_cmd_ident,
2016                                 silc_client_get_server_cb, i);
2017     return server->internal.resolve_cmd_ident;
2018   }
2019
2020   /* Send the command */
2021   idp = silc_id_payload_encode(server_id, SILC_ID_SERVER);
2022   cmd_ident = silc_client_command_send(client, conn, SILC_COMMAND_IDENTIFY,
2023                                        silc_client_get_server_cb, i, 1,
2024                                        5, silc_buffer_datalen(idp));
2025   silc_buffer_free(idp);
2026   if (!cmd_ident && completion)
2027     completion(client, conn, SILC_STATUS_ERR_RESOURCE_LIMIT, NULL, context);
2028
2029   if (server && cmd_ident)
2030     server->internal.resolve_cmd_ident = cmd_ident;
2031
2032   silc_client_unref_server(client, conn, server);
2033
2034   return cmd_ident;
2035 }
2036
2037 /************************** Server Entry Routines ***************************/
2038
2039 /* Add new server entry */
2040
2041 SilcServerEntry silc_client_add_server(SilcClient client,
2042                                        SilcClientConnection conn,
2043                                        const char *server_name,
2044                                        const char *server_info,
2045                                        SilcServerID *server_id)
2046 {
2047   SilcServerEntry server_entry;
2048   char *server_namec = NULL;
2049
2050   if (!server_id)
2051     return NULL;
2052
2053   SILC_LOG_DEBUG(("Adding new server %s", server_name));
2054
2055   server_entry = silc_calloc(1, sizeof(*server_entry));
2056   if (!server_entry)
2057     return NULL;
2058
2059   silc_rwlock_alloc(&server_entry->internal.lock);
2060   silc_atomic_init8(&server_entry->internal.refcnt, 0);
2061   server_entry->id = *server_id;
2062   if (server_name)
2063     server_entry->server_name = strdup(server_name);
2064   if (server_info)
2065     server_entry->server_info = strdup(server_info);
2066
2067   /* Normalize server name */
2068   if (server_name) {
2069     server_namec = silc_identifier_check(server_name, strlen(server_name),
2070                                          SILC_STRING_UTF8, 256, NULL);
2071     if (!server_namec) {
2072       silc_free(server_entry->server_name);
2073       silc_free(server_entry->server_info);
2074       silc_free(server_entry);
2075       return NULL;
2076     }
2077   }
2078
2079   silc_mutex_lock(conn->internal->lock);
2080
2081   /* Add server to cache */
2082   if (!silc_idcache_add(conn->internal->server_cache, server_namec,
2083                         &server_entry->id, server_entry)) {
2084     silc_free(server_namec);
2085     silc_free(server_entry->server_name);
2086     silc_free(server_entry->server_info);
2087     silc_free(server_entry);
2088     silc_mutex_unlock(conn->internal->lock);
2089     return NULL;
2090   }
2091
2092   silc_mutex_unlock(conn->internal->lock);
2093   silc_client_ref_server(client, conn, server_entry);
2094
2095   SILC_LOG_DEBUG(("Added %p", server_entry));
2096
2097   return server_entry;
2098 }
2099
2100 /* Removes server from the cache by the server entry. */
2101
2102 SilcBool silc_client_del_server(SilcClient client, SilcClientConnection conn,
2103                                 SilcServerEntry server)
2104 {
2105   SilcIDCacheEntry id_cache;
2106   SilcBool ret = TRUE;
2107   char *namec;
2108
2109   if (!server)
2110     return FALSE;
2111
2112   if (silc_atomic_sub_int8(&server->internal.refcnt, 1) > 0)
2113     return FALSE;
2114
2115   SILC_LOG_DEBUG(("Deleting server %p", server));
2116
2117   silc_mutex_lock(conn->internal->lock);
2118   if (silc_idcache_find_by_context(conn->internal->server_cache, server,
2119                                    &id_cache)) {
2120     namec = id_cache->name;
2121     ret = silc_idcache_del_by_context(conn->internal->server_cache,
2122                                       server, NULL);
2123     silc_free(namec);
2124   }
2125   silc_mutex_unlock(conn->internal->lock);
2126
2127   silc_free(server->server_name);
2128   silc_free(server->server_info);
2129   if (server->public_key)
2130     silc_pkcs_public_key_free(server->public_key);
2131   silc_atomic_uninit8(&server->internal.refcnt);
2132   silc_rwlock_free(server->internal.lock);
2133   silc_free(server);
2134
2135   return ret;
2136 }
2137
2138 /* Updates the `server_entry' with the new information sent as argument. */
2139
2140 void silc_client_update_server(SilcClient client,
2141                                SilcClientConnection conn,
2142                                SilcServerEntry server_entry,
2143                                const char *server_name,
2144                                const char *server_info)
2145 {
2146   char *server_namec = NULL;
2147
2148   SILC_LOG_DEBUG(("Updating server %p", server_entry));
2149
2150   if (server_name &&
2151       (!server_entry->server_name ||
2152        !silc_utf8_strcasecmp(server_entry->server_name, server_name))) {
2153
2154     server_namec = silc_identifier_check(server_name, strlen(server_name),
2155                                          SILC_STRING_UTF8, 256, NULL);
2156     if (!server_namec)
2157       return;
2158
2159     silc_free(server_entry->server_name);
2160     server_entry->server_name = strdup(server_name);
2161     if (!server_entry->server_name)
2162       return;
2163
2164     /* Update cache entry */
2165     silc_mutex_lock(conn->internal->lock);
2166     silc_idcache_update_by_context(conn->internal->server_cache, server_entry,
2167                                    NULL, server_namec, TRUE);
2168     silc_mutex_unlock(conn->internal->lock);
2169   }
2170
2171   if (server_info &&
2172       (!server_entry->server_info ||
2173        !silc_utf8_strcasecmp(server_entry->server_info, server_info))) {
2174     silc_free(server_entry->server_info);
2175     server_entry->server_info = strdup(server_info);
2176   }
2177 }
2178
2179 /* Lock server */
2180
2181 void silc_client_lock_server(SilcServerEntry server_entry)
2182 {
2183   silc_rwlock_rdlock(server_entry->internal.lock);
2184 }
2185
2186 /* Unlock server */
2187
2188 void silc_client_unlock_server(SilcServerEntry server_entry)
2189 {
2190   silc_rwlock_unlock(server_entry->internal.lock);
2191 }
2192
2193 /* Take reference of server entry */
2194
2195 SilcServerEntry silc_client_ref_server(SilcClient client,
2196                                        SilcClientConnection conn,
2197                                        SilcServerEntry server_entry)
2198 {
2199   silc_atomic_add_int8(&server_entry->internal.refcnt, 1);
2200   SILC_LOG_DEBUG(("Server %p refcnt %d->%d", server_entry,
2201                   silc_atomic_get_int8(&server_entry->internal.refcnt) - 1,
2202                   silc_atomic_get_int8(&server_entry->internal.refcnt)));
2203   return server_entry;
2204 }
2205
2206 /* Release reference of server entry */
2207
2208 void silc_client_unref_server(SilcClient client, SilcClientConnection conn,
2209                               SilcServerEntry server_entry)
2210 {
2211   if (server_entry) {
2212     SILC_LOG_DEBUG(("Server %p refcnt %d->%d", server_entry,
2213                     silc_atomic_get_int8(&server_entry->internal.refcnt),
2214                     silc_atomic_get_int8(&server_entry->internal.refcnt)
2215                     - 1));
2216     silc_client_del_server(client, conn, server_entry);
2217   }
2218 }
2219
2220 /* Free server entry list */
2221
2222 void silc_client_list_free_servers(SilcClient client,
2223                                    SilcClientConnection conn,
2224                                    SilcDList server_list)
2225 {
2226   SilcServerEntry server_entry;
2227
2228   if (server_list) {
2229     silc_dlist_start(server_list);
2230     while ((server_entry = silc_dlist_get(server_list)))
2231       silc_client_unref_server(client, conn, server_entry);
2232
2233     silc_dlist_uninit(server_list);
2234   }
2235 }