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