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