Fixed nickname parsing.
[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 (client_entry->nickname_normalized &&
1129       !silc_utf8_strcasecmp(client_entry->nickname,
1130                             client_entry->nickname_normalized))
1131     formatted = TRUE;
1132
1133   if (client->internal->params->nickname_force_format)
1134     formatted = FALSE;
1135
1136   /* Find unformatted client entry */
1137   while ((entry = silc_dlist_get(clients))) {
1138     if (!entry->internal.valid)
1139       continue;
1140     if (entry == client_entry)
1141       continue;
1142     if (silc_utf8_strcasecmp(entry->nickname, entry->nickname_normalized)) {
1143       unformatted = entry;
1144       break;
1145     }
1146   }
1147
1148   /* If there are no other unformatted clients and the requested client is
1149      unformatted, just return it. */
1150   if (!unformatted && !formatted) {
1151     silc_client_list_free(client, conn, clients);
1152     return client_entry;
1153   }
1154
1155   /* If priority formatting then the requested client will get the
1156      unformatted nickname, and the unformatted client will get a new
1157      formatted nickname. */
1158   if (priority) {
1159     if (formatted) {
1160       /* Simply change the client's nickname to unformatted */
1161       if (!silc_client_nickname_parse(client, conn, client_entry->nickname,
1162                                       &cp))
1163         return NULL;
1164
1165       silc_snprintf(client_entry->nickname, sizeof(client_entry->nickname),
1166                     cp);
1167       silc_free(cp);
1168     }
1169
1170     if (!unformatted) {
1171       /* There was no other unformatted client */
1172       silc_client_list_free(client, conn, clients);
1173       return client_entry;
1174     }
1175
1176     /* Now format the previously unformatted client */
1177     client_entry = unformatted;
1178     formatted = FALSE;
1179   }
1180
1181   /* If already formatted just return it */
1182   if (formatted) {
1183     silc_client_list_free(client, conn, clients);
1184     return client_entry;
1185   }
1186
1187   memset(newnick, 0, sizeof(newnick));
1188   cp = client->internal->params->nickname_format;
1189   while (cp && *cp) {
1190     if (*cp == '%') {
1191       cp++;
1192       continue;
1193     }
1194
1195     switch(*cp) {
1196     case 'n':
1197       /* Nickname */
1198       if (!client_entry->nickname[0])
1199         break;
1200       len = strlen(client_entry->nickname);
1201       memcpy(&newnick[off], client_entry->nickname, len);
1202       off += len;
1203       break;
1204     case 'h':
1205       /* Stripped hostname */
1206       if (!client_entry->hostname[0])
1207         break;
1208       len = strcspn(client_entry->hostname, ".");
1209       i = strcspn(client_entry->hostname, "-");
1210       if (i < len)
1211         len = i;
1212       memcpy(&newnick[off], client_entry->hostname, len);
1213       off += len;
1214       break;
1215     case 'H':
1216       /* Full hostname */
1217       if (!client_entry->hostname[0])
1218         break;
1219       len = strlen(client_entry->hostname);
1220       memcpy(&newnick[off], client_entry->hostname, len);
1221       off += len;
1222       break;
1223     case 'a':
1224       /* Ascending number */
1225       {
1226         char tmp[6];
1227         int num, max = 1;
1228
1229         if (silc_dlist_count(clients) == 1)
1230           break;
1231
1232         silc_dlist_start(clients);
1233         while ((entry = silc_dlist_get(clients))) {
1234           if (!silc_utf8_strncasecmp(entry->nickname, newnick, off))
1235             continue;
1236           if (strlen(entry->nickname) <= off)
1237             continue;
1238           num = atoi(&entry->nickname[off]);
1239           if (num > max)
1240             max = num;
1241         }
1242
1243         memset(tmp, 0, sizeof(tmp));
1244         silc_snprintf(tmp, sizeof(tmp) - 1, "%d", ++max);
1245         len = strlen(tmp);
1246         memcpy(&newnick[off], tmp, len);
1247         off += len;
1248       }
1249       break;
1250     default:
1251       /* Some other character in the string */
1252       memcpy(&newnick[off], cp, 1);
1253       off++;
1254       break;
1255     }
1256
1257     cp++;
1258   }
1259
1260   newnick[off] = 0;
1261   memcpy(client_entry->nickname, newnick, strlen(newnick));
1262   silc_client_list_free(client, conn, clients);
1263
1264   return client_entry;
1265 }
1266
1267 /* Parses nickname according to nickname format string */
1268
1269 SilcBool silc_client_nickname_parse(SilcClient client,
1270                                     SilcClientConnection conn,
1271                                     char *nickname,
1272                                     char **ret_nick)
1273 {
1274   char *cp, s = 0, e = 0, *nick;
1275   SilcBool n = FALSE;
1276   int len;
1277
1278   if (!client->internal->params->nickname_format[0]) {
1279     *ret_nick = NULL;
1280     return TRUE;
1281   }
1282
1283   if (!nickname || !nickname[0])
1284     return FALSE;
1285
1286   cp = client->internal->params->nickname_format;
1287   while (cp && *cp) {
1288     if (*cp == '%') {
1289       cp++;
1290       continue;
1291     }
1292
1293     switch(*cp) {
1294     case 'n':
1295       n = TRUE;
1296       break;
1297
1298     case 'h':
1299     case 'H':
1300     case 'a':
1301       break;
1302
1303     default:
1304       /* Get separator character */
1305       if (n)
1306         e = *cp;
1307       else
1308         s = *cp;
1309       break;
1310     }
1311
1312     cp++;
1313   }
1314   if (!n)
1315     return FALSE;
1316
1317   /* Parse the nickname */
1318   nick = nickname;
1319   len = strlen(nick);
1320   if (s)
1321     if (strchr(nickname, s))
1322       nick = strchr(nickname, s) + 1;
1323   if (e)
1324     if (strchr(nick, e))
1325       len = strchr(nick, e) - nick;
1326   if (!len)
1327     return FALSE;
1328
1329   *ret_nick = silc_memdup(nick, len);
1330   if (!(*ret_nick))
1331     return FALSE;
1332
1333   SILC_LOG_DEBUG(("Parsed nickname: %s", *ret_nick));
1334
1335   return TRUE;
1336 }
1337
1338 /************************ Channel Searching Locally *************************/
1339
1340 /* Finds entry for channel by the channel name. Returns the entry or NULL
1341    if the entry was not found. It is found only if the client is joined
1342    to the channel. */
1343
1344 SilcChannelEntry silc_client_get_channel(SilcClient client,
1345                                          SilcClientConnection conn,
1346                                          char *channel)
1347 {
1348   SilcIDCacheEntry id_cache;
1349   SilcChannelEntry entry;
1350
1351   if (!client || !conn || !channel)
1352     return NULL;
1353
1354   SILC_LOG_DEBUG(("Find channel %s", channel));
1355
1356   /* Normalize name for search */
1357   channel = silc_channel_name_check(channel, strlen(channel), SILC_STRING_UTF8,
1358                                     256, NULL);
1359   if (!channel)
1360     return NULL;
1361
1362   silc_mutex_lock(conn->internal->lock);
1363
1364   if (!silc_idcache_find_by_name_one(conn->internal->channel_cache, channel,
1365                                      &id_cache)) {
1366     silc_mutex_unlock(conn->internal->lock);
1367     silc_free(channel);
1368     return NULL;
1369   }
1370
1371   SILC_LOG_DEBUG(("Found"));
1372
1373   entry = id_cache->context;
1374
1375   /* Reference */
1376   silc_client_ref_channel(client, conn, entry);
1377   silc_mutex_unlock(conn->internal->lock);
1378
1379   silc_free(channel);
1380
1381   return entry;
1382 }
1383
1384 /* Finds entry for channel by the channel ID. Returns the entry or NULL
1385    if the entry was not found. It is found only if the client is joined
1386    to the channel. */
1387
1388 SilcChannelEntry silc_client_get_channel_by_id(SilcClient client,
1389                                                SilcClientConnection conn,
1390                                                SilcChannelID *channel_id)
1391 {
1392   SilcIDCacheEntry id_cache;
1393   SilcChannelEntry entry;
1394
1395   if (!client || !conn || !channel_id)
1396     return NULL;
1397
1398   SILC_LOG_DEBUG(("Find channel by id %s",
1399                   silc_id_render(channel_id, SILC_ID_CHANNEL)));
1400
1401   silc_mutex_lock(conn->internal->lock);
1402
1403   if (!silc_idcache_find_by_id_one(conn->internal->channel_cache, channel_id,
1404                                    &id_cache)) {
1405     silc_mutex_unlock(conn->internal->lock);
1406     return NULL;
1407   }
1408
1409   SILC_LOG_DEBUG(("Found"));
1410
1411   entry = id_cache->context;
1412
1413   /* Reference */
1414   silc_client_ref_channel(client, conn, entry);
1415   silc_mutex_unlock(conn->internal->lock);
1416
1417   return entry;
1418 }
1419
1420 /********************** Channel Resolving from Server ***********************/
1421
1422 /* Channel resolving context */
1423 typedef struct {
1424   SilcDList channels;
1425   SilcGetChannelCallback completion;
1426   void *context;
1427 } *SilcClientGetChannelInternal;
1428
1429 /* Resolving command callback */
1430
1431 static SilcBool silc_client_get_channel_cb(SilcClient client,
1432                                            SilcClientConnection conn,
1433                                            SilcCommand command,
1434                                            SilcStatus status,
1435                                            SilcStatus error,
1436                                            void *context,
1437                                            va_list ap)
1438 {
1439   SilcClientGetChannelInternal i = context;
1440   SilcChannelEntry entry;
1441
1442   if (error != SILC_STATUS_OK) {
1443     SILC_LOG_DEBUG(("Resolving failed: %s", silc_get_status_message(error)));
1444     if (i->completion)
1445       i->completion(client, conn, error, NULL, i->context);
1446     goto out;
1447   }
1448
1449   /* Add the returned channel to list */
1450   if (i->completion) {
1451     entry = va_arg(ap, SilcChannelEntry);
1452     silc_client_ref_channel(client, conn, entry);
1453     silc_dlist_add(i->channels, entry);
1454   }
1455
1456   if (status == SILC_STATUS_OK || status == SILC_STATUS_LIST_END) {
1457     /* Deliver the channels to the caller */
1458     if (i->completion) {
1459       SILC_LOG_DEBUG(("Resolved %d channels", silc_dlist_count(i->channels)));
1460       silc_dlist_start(i->channels);
1461       i->completion(client, conn, SILC_STATUS_OK, i->channels, i->context);
1462     }
1463     goto out;
1464   }
1465
1466   return TRUE;
1467
1468  out:
1469   silc_client_list_free_channels(client, conn, i->channels);
1470   silc_free(i);
1471   return FALSE;
1472 }
1473
1474 /* Resolves channel entry from the server by the channel name. */
1475
1476 void silc_client_get_channel_resolve(SilcClient client,
1477                                      SilcClientConnection conn,
1478                                      char *channel_name,
1479                                      SilcGetChannelCallback completion,
1480                                      void *context)
1481 {
1482   SilcClientGetChannelInternal i;
1483
1484   if (!client || !conn || !channel_name || !completion)
1485     return;
1486
1487   SILC_LOG_DEBUG(("Resolve channel %s", channel_name));
1488
1489   i = silc_calloc(1, sizeof(*i));
1490   if (!i)
1491     return;
1492   i->completion = completion;
1493   i->context = context;
1494   i->channels = silc_dlist_init();
1495   if (!i->channels) {
1496     silc_free(i);
1497     return;
1498   }
1499
1500   /* Send the command */
1501   if (!silc_client_command_send(client, conn, SILC_COMMAND_IDENTIFY,
1502                                 silc_client_get_channel_cb, i, 1,
1503                                 3, channel_name, strlen(channel_name))) {
1504     if (completion)
1505       completion(client, conn, SILC_STATUS_ERR_RESOURCE_LIMIT, NULL, context);
1506   }
1507 }
1508
1509 /* Resolves channel information from the server by the channel ID. */
1510
1511 SilcUInt16
1512 silc_client_get_channel_by_id_resolve(SilcClient client,
1513                                       SilcClientConnection conn,
1514                                       SilcChannelID *channel_id,
1515                                       SilcGetChannelCallback completion,
1516                                       void *context)
1517 {
1518   SilcClientGetChannelInternal i;
1519   SilcBuffer idp;
1520   SilcUInt16 cmd_ident;
1521
1522   if (!client || !conn || !channel_id || !completion)
1523     return 0;
1524
1525   SILC_LOG_DEBUG(("Resolve channel by id %s",
1526                   silc_id_render(channel_id, SILC_ID_CHANNEL)));
1527
1528   i = silc_calloc(1, sizeof(*i));
1529   if (!i)
1530     return 0;
1531   i->completion = completion;
1532   i->context = context;
1533   i->channels = silc_dlist_init();
1534   if (!i->channels) {
1535     silc_free(i);
1536     return 0;
1537   }
1538
1539   /* Send the command */
1540   idp = silc_id_payload_encode(channel_id, SILC_ID_CHANNEL);
1541   cmd_ident = silc_client_command_send(client, conn, SILC_COMMAND_IDENTIFY,
1542                                        silc_client_get_channel_cb, i, 1,
1543                                        5, silc_buffer_datalen(idp));
1544   silc_buffer_free(idp);
1545   if (!cmd_ident && completion)
1546     completion(client, conn, SILC_STATUS_ERR_RESOURCE_LIMIT, NULL, context);
1547
1548   return cmd_ident;
1549 }
1550
1551 /************************* Channel Entry Routines ***************************/
1552
1553 /* Add new channel entry to the ID Cache */
1554
1555 SilcChannelEntry silc_client_add_channel(SilcClient client,
1556                                          SilcClientConnection conn,
1557                                          const char *channel_name,
1558                                          SilcUInt32 mode,
1559                                          SilcChannelID *channel_id)
1560 {
1561   SilcChannelEntry channel;
1562   char *channel_namec;
1563
1564   SILC_LOG_DEBUG(("Start"));
1565
1566   channel = silc_calloc(1, sizeof(*channel));
1567   if (!channel)
1568     return NULL;
1569
1570   silc_rwlock_alloc(&channel->internal.lock);
1571   silc_atomic_init16(&channel->internal.refcnt, 0);
1572   channel->id = *channel_id;
1573   channel->mode = mode;
1574
1575   channel->channel_name = strdup(channel_name);
1576   if (!channel->channel_name) {
1577     silc_free(channel);
1578     return NULL;
1579   }
1580
1581   channel->user_list = silc_hash_table_alloc(1, silc_hash_ptr, NULL, NULL,
1582                                              NULL, NULL, NULL, TRUE);
1583   if (!channel->user_list) {
1584     silc_free(channel->channel_name);
1585     silc_free(channel);
1586     return NULL;
1587   }
1588
1589   /* Normalize channel name */
1590   channel_namec = silc_channel_name_check(channel_name, strlen(channel_name),
1591                                           SILC_STRING_UTF8, 256, NULL);
1592   if (!channel_namec) {
1593     silc_free(channel->channel_name);
1594     silc_hash_table_free(channel->user_list);
1595     silc_free(channel);
1596     return NULL;
1597   }
1598
1599   silc_mutex_lock(conn->internal->lock);
1600
1601   /* Add channel to cache, the normalized channel name is saved to cache */
1602   if (!silc_idcache_add(conn->internal->channel_cache, channel_namec,
1603                         &channel->id, channel)) {
1604     silc_free(channel_namec);
1605     silc_free(channel->channel_name);
1606     silc_hash_table_free(channel->user_list);
1607     silc_free(channel);
1608     silc_mutex_unlock(conn->internal->lock);
1609     return NULL;
1610   }
1611
1612   silc_mutex_unlock(conn->internal->lock);
1613   silc_client_ref_channel(client, conn, channel);
1614
1615   SILC_LOG_DEBUG(("Added %p", channel));
1616
1617   return channel;
1618 }
1619
1620 /* Removes channel from the cache by the channel entry. */
1621
1622 SilcBool silc_client_del_channel(SilcClient client, SilcClientConnection conn,
1623                                  SilcChannelEntry channel)
1624 {
1625   SilcIDCacheEntry id_cache;
1626   SilcBool ret = TRUE;
1627   SilcCipher key;
1628   SilcHmac hmac;
1629   char *namec;
1630
1631   if (!channel)
1632     return FALSE;
1633
1634   if (silc_atomic_sub_int16(&channel->internal.refcnt, 1) > 0)
1635     return FALSE;
1636
1637   SILC_LOG_DEBUG(("Deleting channel %p", channel));
1638
1639   silc_mutex_lock(conn->internal->lock);
1640   if (silc_idcache_find_by_context(conn->internal->channel_cache, channel,
1641                                    &id_cache)) {
1642     namec = id_cache->name;
1643     ret = silc_idcache_del_by_context(conn->internal->channel_cache,
1644                                       channel, NULL);
1645     silc_free(namec);
1646   }
1647   silc_mutex_unlock(conn->internal->lock);
1648
1649   if (!ret)
1650     return FALSE;
1651
1652   silc_client_empty_channel(client, conn, channel);
1653   silc_hash_table_free(channel->user_list);
1654   silc_free(channel->channel_name);
1655   silc_free(channel->topic);
1656   if (channel->founder_key)
1657     silc_pkcs_public_key_free(channel->founder_key);
1658   if (channel->internal.send_key)
1659     silc_cipher_free(channel->internal.send_key);
1660   if (channel->internal.receive_key)
1661     silc_cipher_free(channel->internal.receive_key);
1662   if (channel->internal.hmac)
1663     silc_hmac_free(channel->internal.hmac);
1664   if (channel->internal.old_channel_keys) {
1665     silc_dlist_start(channel->internal.old_channel_keys);
1666     while ((key = silc_dlist_get(channel->internal.old_channel_keys)))
1667       silc_cipher_free(key);
1668     silc_dlist_uninit(channel->internal.old_channel_keys);
1669   }
1670   if (channel->internal.old_hmacs) {
1671     silc_dlist_start(channel->internal.old_hmacs);
1672     while ((hmac = silc_dlist_get(channel->internal.old_hmacs)))
1673       silc_hmac_free(hmac);
1674     silc_dlist_uninit(channel->internal.old_hmacs);
1675   }
1676   if (channel->channel_pubkeys)
1677     silc_argument_list_free(channel->channel_pubkeys,
1678                             SILC_ARGUMENT_PUBLIC_KEY);
1679   silc_client_del_channel_private_keys(client, conn, channel);
1680   silc_atomic_uninit16(&channel->internal.refcnt);
1681   silc_rwlock_free(channel->internal.lock);
1682   silc_schedule_task_del_by_context(conn->client->schedule, channel);
1683   silc_free(channel);
1684
1685   return ret;
1686 }
1687
1688 /* Replaces the channel ID of the `channel' to `new_id'. Returns FALSE
1689    if the ID could not be changed.  This handles entry locking internally. */
1690
1691 SilcBool silc_client_replace_channel_id(SilcClient client,
1692                                         SilcClientConnection conn,
1693                                         SilcChannelEntry channel,
1694                                         SilcChannelID *new_id)
1695 {
1696   SilcBool ret = FALSE;
1697
1698   if (!new_id)
1699     return FALSE;
1700
1701   SILC_LOG_DEBUG(("Old Channel ID id(%s)",
1702                   silc_id_render(&channel->id, SILC_ID_CHANNEL)));
1703   SILC_LOG_DEBUG(("New Channel ID id(%s)",
1704                   silc_id_render(new_id, SILC_ID_CHANNEL)));
1705
1706   /* Update the ID */
1707   silc_rwlock_wrlock(channel->internal.lock);
1708   silc_mutex_lock(conn->internal->lock);
1709   silc_idcache_update_by_context(conn->internal->channel_cache, channel,
1710                                  new_id, NULL, FALSE);
1711   silc_mutex_unlock(conn->internal->lock);
1712   silc_rwlock_unlock(channel->internal.lock);
1713
1714   return ret;
1715 }
1716
1717 /* Lock channel */
1718
1719 void silc_client_lock_channel(SilcChannelEntry channel_entry)
1720 {
1721   silc_rwlock_rdlock(channel_entry->internal.lock);
1722 }
1723
1724 /* Unlock channel */
1725
1726 void silc_client_unlock_channel(SilcChannelEntry channel_entry)
1727 {
1728   silc_rwlock_unlock(channel_entry->internal.lock);
1729 }
1730
1731 /* Take reference of channel entry */
1732
1733 SilcChannelEntry silc_client_ref_channel(SilcClient client,
1734                                          SilcClientConnection conn,
1735                                          SilcChannelEntry channel_entry)
1736 {
1737   silc_atomic_add_int16(&channel_entry->internal.refcnt, 1);
1738   SILC_LOG_DEBUG(("Channel %p refcnt %d->%d", channel_entry,
1739                   silc_atomic_get_int16(&channel_entry->internal.refcnt) - 1,
1740                   silc_atomic_get_int16(&channel_entry->internal.refcnt)));
1741   return channel_entry;
1742 }
1743
1744 /* Release reference of channel entry */
1745
1746 void silc_client_unref_channel(SilcClient client, SilcClientConnection conn,
1747                                SilcChannelEntry channel_entry)
1748 {
1749   if (channel_entry) {
1750     SILC_LOG_DEBUG(("Channel %p refcnt %d->%d", channel_entry,
1751                     silc_atomic_get_int16(&channel_entry->internal.refcnt),
1752                     silc_atomic_get_int16(&channel_entry->internal.refcnt)
1753                     - 1));
1754     silc_client_del_channel(client, conn, channel_entry);
1755   }
1756 }
1757
1758 /* Free channel entry list */
1759
1760 void silc_client_list_free_channels(SilcClient client,
1761                                     SilcClientConnection conn,
1762                                     SilcDList channel_list)
1763 {
1764   SilcChannelEntry channel_entry;
1765
1766   if (channel_list) {
1767     silc_dlist_start(channel_list);
1768     while ((channel_entry = silc_dlist_get(channel_list)))
1769       silc_client_unref_channel(client, conn, channel_entry);
1770
1771     silc_dlist_uninit(channel_list);
1772   }
1773 }
1774
1775 /************************* Server Searching Locally *************************/
1776
1777 /* Finds entry for server by the server name. */
1778
1779 SilcServerEntry silc_client_get_server(SilcClient client,
1780                                        SilcClientConnection conn,
1781                                        char *server_name)
1782 {
1783   SilcIDCacheEntry id_cache;
1784   SilcServerEntry entry;
1785
1786   if (!client || !conn || !server_name)
1787     return NULL;
1788
1789   SILC_LOG_DEBUG(("Find server by name %s", server_name));
1790
1791   /* Normalize server name for search */
1792   server_name = silc_identifier_check(server_name, strlen(server_name),
1793                                       SILC_STRING_UTF8, 256, NULL);
1794   if (!server_name)
1795     return NULL;
1796
1797   silc_mutex_lock(conn->internal->lock);
1798
1799   if (!silc_idcache_find_by_name_one(conn->internal->server_cache,
1800                                      server_name, &id_cache)) {
1801     silc_free(server_name);
1802     silc_mutex_unlock(conn->internal->lock);
1803     return NULL;
1804   }
1805
1806   SILC_LOG_DEBUG(("Found"));
1807
1808   /* Reference */
1809   entry = id_cache->context;
1810   silc_client_ref_server(client, conn, entry);
1811
1812   silc_mutex_unlock(conn->internal->lock);
1813
1814   silc_free(server_name);
1815
1816   return entry;
1817 }
1818
1819 /* Finds entry for server by the server ID. */
1820
1821 SilcServerEntry silc_client_get_server_by_id(SilcClient client,
1822                                              SilcClientConnection conn,
1823                                              SilcServerID *server_id)
1824 {
1825   SilcIDCacheEntry id_cache;
1826   SilcServerEntry entry;
1827
1828   if (!client || !conn || !server_id)
1829     return NULL;
1830
1831   SILC_LOG_DEBUG(("Find server by id %s",
1832                   silc_id_render(server_id, SILC_ID_SERVER)));
1833
1834   silc_mutex_lock(conn->internal->lock);
1835
1836   if (!silc_idcache_find_by_id_one(conn->internal->server_cache,
1837                                    server_id, &id_cache)) {
1838     silc_mutex_unlock(conn->internal->lock);
1839     return NULL;
1840   }
1841
1842   SILC_LOG_DEBUG(("Found"));
1843
1844   /* Reference */
1845   entry = id_cache->context;
1846   silc_client_ref_server(client, conn, entry);
1847
1848   silc_mutex_unlock(conn->internal->lock);
1849
1850   return entry;
1851 }
1852
1853 /*********************** Server Resolving from Server ***********************/
1854
1855 /* Resolving context */
1856 typedef struct {
1857   SilcDList servers;
1858   SilcGetServerCallback completion;
1859   void *context;
1860 } *SilcClientGetServerInternal;
1861
1862 /* Resolving command callback */
1863
1864 static SilcBool silc_client_get_server_cb(SilcClient client,
1865                                           SilcClientConnection conn,
1866                                           SilcCommand command,
1867                                           SilcStatus status,
1868                                           SilcStatus error,
1869                                           void *context,
1870                                           va_list ap)
1871 {
1872   SilcClientGetServerInternal i = context;
1873   SilcServerEntry server;
1874
1875   if (error != SILC_STATUS_OK) {
1876     SILC_LOG_DEBUG(("Resolving failed: %s", silc_get_status_message(error)));
1877     if (i->completion)
1878       i->completion(client, conn, error, NULL, i->context);
1879     goto out;
1880   }
1881
1882   /* Add the returned servers to list */
1883   if (i->completion) {
1884     server = va_arg(ap, SilcServerEntry);
1885     silc_client_ref_server(client, conn, server);
1886     silc_dlist_add(i->servers, server);
1887     server->internal.resolve_cmd_ident = 0;
1888   }
1889
1890   if (status == SILC_STATUS_OK || status == SILC_STATUS_LIST_END) {
1891     /* Deliver the servers to the caller */
1892     if (i->completion) {
1893       SILC_LOG_DEBUG(("Resolved %d servers", silc_dlist_count(i->servers)));
1894       silc_dlist_start(i->servers);
1895       i->completion(client, conn, SILC_STATUS_OK, i->servers, i->context);
1896     }
1897     goto out;
1898   }
1899
1900   return TRUE;
1901
1902  out:
1903   silc_client_list_free_servers(client, conn, i->servers);
1904   silc_free(i);
1905   return FALSE;
1906 }
1907
1908 /* Resolve server by server ID */
1909
1910 SilcUInt16
1911 silc_client_get_server_by_id_resolve(SilcClient client,
1912                                      SilcClientConnection conn,
1913                                      SilcServerID *server_id,
1914                                      SilcGetServerCallback completion,
1915                                      void *context)
1916 {
1917   SilcClientGetServerInternal i;
1918   SilcServerEntry server;
1919   SilcBuffer idp;
1920   SilcUInt16 cmd_ident;
1921
1922   if (!client || !conn || !server_id || !completion)
1923     return 0;
1924
1925   SILC_LOG_DEBUG(("Resolve server by id %s",
1926                   silc_id_render(server_id, SILC_ID_SERVER)));
1927
1928   i = silc_calloc(1, sizeof(*i));
1929   if (!i)
1930     return 0;
1931   i->completion = completion;
1932   i->context = context;
1933   i->servers = silc_dlist_init();
1934   if (!i->servers) {
1935     silc_free(i);
1936     return 0;
1937   }
1938
1939   /* Attach to resolving, if on going */
1940   server = silc_client_get_server_by_id(client, conn, server_id);
1941   if (server && server->internal.resolve_cmd_ident) {
1942     SILC_LOG_DEBUG(("Attach to existing resolving"));
1943     silc_client_unref_server(client, conn, server);
1944     silc_client_command_pending(conn, SILC_COMMAND_NONE,
1945                                 server->internal.resolve_cmd_ident,
1946                                 silc_client_get_server_cb, i);
1947     return server->internal.resolve_cmd_ident;
1948   }
1949
1950   /* Send the command */
1951   idp = silc_id_payload_encode(server_id, SILC_ID_SERVER);
1952   cmd_ident = silc_client_command_send(client, conn, SILC_COMMAND_IDENTIFY,
1953                                        silc_client_get_server_cb, i, 1,
1954                                        5, silc_buffer_datalen(idp));
1955   silc_buffer_free(idp);
1956   if (!cmd_ident && completion)
1957     completion(client, conn, SILC_STATUS_ERR_RESOURCE_LIMIT, NULL, context);
1958
1959   if (server && cmd_ident)
1960     server->internal.resolve_cmd_ident = cmd_ident;
1961
1962   silc_client_unref_server(client, conn, server);
1963
1964   return cmd_ident;
1965 }
1966
1967 /************************** Server Entry Routines ***************************/
1968
1969 /* Add new server entry */
1970
1971 SilcServerEntry silc_client_add_server(SilcClient client,
1972                                        SilcClientConnection conn,
1973                                        const char *server_name,
1974                                        const char *server_info,
1975                                        SilcServerID *server_id)
1976 {
1977   SilcServerEntry server_entry;
1978   char *server_namec = NULL;
1979
1980   if (!server_id)
1981     return NULL;
1982
1983   SILC_LOG_DEBUG(("Adding new server %s", server_name));
1984
1985   server_entry = silc_calloc(1, sizeof(*server_entry));
1986   if (!server_entry)
1987     return NULL;
1988
1989   silc_rwlock_alloc(&server_entry->internal.lock);
1990   silc_atomic_init8(&server_entry->internal.refcnt, 0);
1991   server_entry->id = *server_id;
1992   if (server_name)
1993     server_entry->server_name = strdup(server_name);
1994   if (server_info)
1995     server_entry->server_info = strdup(server_info);
1996
1997   /* Normalize server name */
1998   if (server_name) {
1999     server_namec = silc_identifier_check(server_name, strlen(server_name),
2000                                          SILC_STRING_UTF8, 256, NULL);
2001     if (!server_namec) {
2002       silc_free(server_entry->server_name);
2003       silc_free(server_entry->server_info);
2004       silc_free(server_entry);
2005       return NULL;
2006     }
2007   }
2008
2009   silc_mutex_lock(conn->internal->lock);
2010
2011   /* Add server to cache */
2012   if (!silc_idcache_add(conn->internal->server_cache, server_namec,
2013                         &server_entry->id, server_entry)) {
2014     silc_free(server_namec);
2015     silc_free(server_entry->server_name);
2016     silc_free(server_entry->server_info);
2017     silc_free(server_entry);
2018     silc_mutex_unlock(conn->internal->lock);
2019     return NULL;
2020   }
2021
2022   silc_mutex_unlock(conn->internal->lock);
2023   silc_client_ref_server(client, conn, server_entry);
2024
2025   SILC_LOG_DEBUG(("Added %p", server_entry));
2026
2027   return server_entry;
2028 }
2029
2030 /* Removes server from the cache by the server entry. */
2031
2032 SilcBool silc_client_del_server(SilcClient client, SilcClientConnection conn,
2033                                 SilcServerEntry server)
2034 {
2035   SilcIDCacheEntry id_cache;
2036   SilcBool ret = TRUE;
2037   char *namec;
2038
2039   if (!server)
2040     return FALSE;
2041
2042   if (silc_atomic_sub_int8(&server->internal.refcnt, 1) > 0)
2043     return FALSE;
2044
2045   SILC_LOG_DEBUG(("Deleting server %p", server));
2046
2047   silc_mutex_lock(conn->internal->lock);
2048   if (silc_idcache_find_by_context(conn->internal->server_cache, server,
2049                                    &id_cache)) {
2050     namec = id_cache->name;
2051     ret = silc_idcache_del_by_context(conn->internal->server_cache,
2052                                       server, NULL);
2053     silc_free(namec);
2054   }
2055   silc_mutex_unlock(conn->internal->lock);
2056
2057   silc_free(server->server_name);
2058   silc_free(server->server_info);
2059   if (server->public_key)
2060     silc_pkcs_public_key_free(server->public_key);
2061   silc_atomic_uninit8(&server->internal.refcnt);
2062   silc_rwlock_free(server->internal.lock);
2063   silc_free(server);
2064
2065   return ret;
2066 }
2067
2068 /* Updates the `server_entry' with the new information sent as argument. */
2069
2070 void silc_client_update_server(SilcClient client,
2071                                SilcClientConnection conn,
2072                                SilcServerEntry server_entry,
2073                                const char *server_name,
2074                                const char *server_info)
2075 {
2076   char *server_namec = NULL;
2077
2078   SILC_LOG_DEBUG(("Updating server %p", server_entry));
2079
2080   if (server_name &&
2081       (!server_entry->server_name ||
2082        !silc_utf8_strcasecmp(server_entry->server_name, server_name))) {
2083
2084     server_namec = silc_identifier_check(server_name, strlen(server_name),
2085                                          SILC_STRING_UTF8, 256, NULL);
2086     if (!server_namec)
2087       return;
2088
2089     silc_free(server_entry->server_name);
2090     server_entry->server_name = strdup(server_name);
2091     if (!server_entry->server_name)
2092       return;
2093
2094     /* Update cache entry */
2095     silc_mutex_lock(conn->internal->lock);
2096     silc_idcache_update_by_context(conn->internal->server_cache, server_entry,
2097                                    NULL, server_namec, TRUE);
2098     silc_mutex_unlock(conn->internal->lock);
2099   }
2100
2101   if (server_info &&
2102       (!server_entry->server_info ||
2103        !silc_utf8_strcasecmp(server_entry->server_info, server_info))) {
2104     silc_free(server_entry->server_info);
2105     server_entry->server_info = strdup(server_info);
2106   }
2107 }
2108
2109 /* Lock server */
2110
2111 void silc_client_lock_server(SilcServerEntry server_entry)
2112 {
2113   silc_rwlock_rdlock(server_entry->internal.lock);
2114 }
2115
2116 /* Unlock server */
2117
2118 void silc_client_unlock_server(SilcServerEntry server_entry)
2119 {
2120   silc_rwlock_unlock(server_entry->internal.lock);
2121 }
2122
2123 /* Take reference of server entry */
2124
2125 SilcServerEntry silc_client_ref_server(SilcClient client,
2126                                        SilcClientConnection conn,
2127                                        SilcServerEntry server_entry)
2128 {
2129   silc_atomic_add_int8(&server_entry->internal.refcnt, 1);
2130   SILC_LOG_DEBUG(("Server %p refcnt %d->%d", server_entry,
2131                   silc_atomic_get_int8(&server_entry->internal.refcnt) - 1,
2132                   silc_atomic_get_int8(&server_entry->internal.refcnt)));
2133   return server_entry;
2134 }
2135
2136 /* Release reference of server entry */
2137
2138 void silc_client_unref_server(SilcClient client, SilcClientConnection conn,
2139                               SilcServerEntry server_entry)
2140 {
2141   if (server_entry) {
2142     SILC_LOG_DEBUG(("Server %p refcnt %d->%d", server_entry,
2143                     silc_atomic_get_int8(&server_entry->internal.refcnt),
2144                     silc_atomic_get_int8(&server_entry->internal.refcnt)
2145                     - 1));
2146     silc_client_del_server(client, conn, server_entry);
2147   }
2148 }
2149
2150 /* Free server entry list */
2151
2152 void silc_client_list_free_servers(SilcClient client,
2153                                    SilcClientConnection conn,
2154                                    SilcDList server_list)
2155 {
2156   SilcServerEntry server_entry;
2157
2158   if (server_list) {
2159     silc_dlist_start(server_list);
2160     while ((server_entry = silc_dlist_get(server_list)))
2161       silc_client_unref_server(client, conn, server_entry);
2162
2163     silc_dlist_uninit(server_list);
2164   }
2165 }