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