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