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