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