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