003f2fcfd03116ab2d92b18b4c1421dbfc3383e6
[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 - 2008 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_init32(&client_entry->internal.refcnt, 0);
795   silc_atomic_init32(&client_entry->internal.deleted, 1);
796   client_entry->id = *id;
797   client_entry->mode = mode;
798   client_entry->realname = userinfo ? strdup(userinfo) : NULL;
799
800   silc_parse_userfqdn(nickname, parsed, sizeof(parsed),
801                       client_entry->server, sizeof(client_entry->server));
802   if (nickname && client->internal->params->full_nicknames)
803     silc_snprintf(client_entry->nickname, sizeof(client_entry->nickname),
804                   nickname);
805   else if (nickname)
806     silc_snprintf(client_entry->nickname, sizeof(client_entry->nickname),
807                   parsed);
808
809   silc_parse_userfqdn(username, client_entry->username,
810                       sizeof(client_entry->username),
811                       client_entry->hostname,
812                       sizeof(client_entry->hostname));
813
814   client_entry->channels = silc_hash_table_alloc(1, silc_hash_ptr, NULL, NULL,
815                                                  NULL, NULL, NULL, TRUE);
816   if (!client_entry->channels) {
817     silc_free(client_entry->realname);
818     silc_atomic_uninit32(&client_entry->internal.deleted);
819     silc_atomic_uninit32(&client_entry->internal.refcnt);
820     silc_rwlock_free(client_entry->internal.lock);
821     silc_free(client_entry);
822     return NULL;
823   }
824
825   /* Normalize nickname */
826   if (client_entry->nickname[0]) {
827     nick = silc_identifier_check(parsed, strlen(parsed),
828                                  SILC_STRING_UTF8, 128, NULL);
829     if (!nick) {
830       silc_hash_table_free(client_entry->channels);
831       silc_free(client_entry->realname);
832       silc_atomic_uninit32(&client_entry->internal.deleted);
833       silc_atomic_uninit32(&client_entry->internal.refcnt);
834       silc_rwlock_free(client_entry->internal.lock);
835       silc_free(client_entry);
836       return NULL;
837     }
838   }
839
840   silc_mutex_lock(conn->internal->lock);
841
842   /* Add client to cache, the normalized nickname is saved to cache */
843   if (!silc_idcache_add(conn->internal->client_cache, nick,
844                         &client_entry->id, client_entry)) {
845     silc_free(nick);
846     silc_hash_table_free(client_entry->channels);
847     silc_free(client_entry->realname);
848     silc_atomic_uninit32(&client_entry->internal.deleted);
849     silc_atomic_uninit32(&client_entry->internal.refcnt);
850     silc_rwlock_free(client_entry->internal.lock);
851     silc_free(client_entry);
852     silc_mutex_unlock(conn->internal->lock);
853     return NULL;
854   }
855
856   client_entry->nickname_normalized = nick;
857
858   silc_mutex_unlock(conn->internal->lock);
859   silc_client_ref_client(client, conn, client_entry);
860
861   /* Format the nickname */
862   silc_client_nickname_format(client, conn, client_entry, FALSE);
863
864   if (client_entry->nickname[0])
865     client_entry->internal.valid = TRUE;
866
867   SILC_LOG_DEBUG(("Added %p", client_entry));
868
869   return client_entry;
870 }
871
872 /* Updates the `client_entry' with the new information sent as argument.
873    This handles entry locking internally. */
874
875 void silc_client_update_client(SilcClient client,
876                                SilcClientConnection conn,
877                                SilcClientEntry client_entry,
878                                const char *nickname,
879                                const char *username,
880                                const char *userinfo,
881                                SilcUInt32 mode)
882 {
883   char *nick = NULL, parsed[128 + 1];
884
885   SILC_LOG_DEBUG(("Update client entry"));
886
887   silc_rwlock_wrlock(client_entry->internal.lock);
888
889   if (!client_entry->realname && userinfo)
890     client_entry->realname = strdup(userinfo);
891
892   if ((!client_entry->username[0] || !client_entry->hostname[0]) && username)
893     silc_parse_userfqdn(username, client_entry->username,
894                         sizeof(client_entry->username),
895                         client_entry->hostname,
896                         sizeof(client_entry->username));
897
898   if (!client_entry->nickname[0] && nickname) {
899     silc_parse_userfqdn(nickname, parsed, sizeof(parsed),
900                         client_entry->server, sizeof(client_entry->server));
901     if (client->internal->params->full_nicknames)
902       silc_snprintf(client_entry->nickname, sizeof(client_entry->nickname),
903                     nickname);
904     else
905       silc_snprintf(client_entry->nickname, sizeof(client_entry->nickname),
906                     parsed);
907
908     /* Normalize nickname */
909     nick = silc_identifier_check(parsed, strlen(parsed),
910                                  SILC_STRING_UTF8, 128, NULL);
911     if (!nick) {
912       silc_rwlock_unlock(client_entry->internal.lock);
913       return;
914     }
915
916     /* Format nickname */
917     silc_client_nickname_format(client, conn, client_entry,
918                                 client_entry == conn->local_entry);
919
920     /* Update cache entry */
921     silc_mutex_lock(conn->internal->lock);
922     silc_idcache_update_by_context(conn->internal->client_cache,
923                                    client_entry, NULL, nick, TRUE);
924     silc_mutex_unlock(conn->internal->lock);
925     client_entry->nickname_normalized = nick;
926     client_entry->internal.valid = TRUE;
927   }
928   client_entry->mode = mode;
929
930   silc_rwlock_unlock(client_entry->internal.lock);
931 }
932
933 /* Change a client's nickname.  Must be called with `client_entry' locked. */
934
935 SilcBool silc_client_change_nickname(SilcClient client,
936                                      SilcClientConnection conn,
937                                      SilcClientEntry client_entry,
938                                      const char *new_nick,
939                                      SilcClientID *new_id,
940                                      const unsigned char *idp,
941                                      SilcUInt32 idp_len)
942 {
943   char *tmp;
944
945   SILC_LOG_DEBUG(("Change nickname %s to %s", client_entry->nickname,
946                   new_nick));
947
948   /* Normalize nickname */
949   tmp = silc_identifier_check(new_nick, strlen(new_nick),
950                               SILC_STRING_UTF8, 128, NULL);
951   if (!tmp)
952     return FALSE;
953
954   /* Update the client entry */
955   silc_mutex_lock(conn->internal->lock);
956   if (!silc_idcache_update_by_context(conn->internal->client_cache,
957                                       client_entry, new_id, tmp, TRUE)) {
958     silc_free(tmp);
959     silc_mutex_unlock(conn->internal->lock);
960     return FALSE;
961   }
962   silc_mutex_unlock(conn->internal->lock);
963
964   memset(client_entry->nickname, 0, sizeof(client_entry->nickname));
965   memcpy(client_entry->nickname, new_nick, strlen(new_nick));
966   client_entry->nickname_normalized = tmp;
967   silc_client_nickname_format(client, conn, client_entry,
968                               client_entry == conn->local_entry);
969
970   /* For my client entry, update ID and set new ID to packet stream */
971   if (client_entry == conn->local_entry) {
972     if (idp && idp_len) {
973       silc_buffer_enlarge(conn->internal->local_idp, idp_len);
974       silc_buffer_put(conn->internal->local_idp, idp, idp_len);
975     }
976     if (new_id)
977       silc_packet_set_ids(conn->stream, SILC_ID_CLIENT, conn->local_id,
978                           0, NULL);
979   }
980
981   client_entry->internal.valid = TRUE;
982   return TRUE;
983 }
984
985 /* Deletes the client entry and frees all memory. */
986
987 void silc_client_del_client_entry(SilcClient client,
988                                   SilcClientConnection conn,
989                                   SilcClientEntry client_entry)
990 {
991   silc_free(client_entry->realname);
992   silc_free(client_entry->nickname_normalized);
993   silc_free(client_entry->internal.key);
994   if (client_entry->public_key)
995     silc_pkcs_public_key_free(client_entry->public_key);
996   silc_hash_table_free(client_entry->channels);
997   if (client_entry->internal.send_key)
998     silc_cipher_free(client_entry->internal.send_key);
999   if (client_entry->internal.receive_key)
1000     silc_cipher_free(client_entry->internal.receive_key);
1001   if (client_entry->internal.hmac_send)
1002     silc_hmac_free(client_entry->internal.hmac_send);
1003   if (client_entry->internal.hmac_receive)
1004     silc_hmac_free(client_entry->internal.hmac_receive);
1005   silc_client_ftp_session_free_client(client, client_entry);
1006   if (client_entry->internal.ke)
1007     silc_client_abort_key_agreement(client, conn, client_entry);
1008   silc_atomic_uninit32(&client_entry->internal.deleted);
1009   silc_atomic_uninit32(&client_entry->internal.refcnt);
1010   silc_rwlock_free(client_entry->internal.lock);
1011   silc_free(client_entry);
1012 }
1013
1014 /* Removes client from the cache by the client entry. */
1015
1016 SilcBool silc_client_del_client(SilcClient client, SilcClientConnection conn,
1017                                 SilcClientEntry client_entry)
1018 {
1019   if (!client_entry)
1020     return FALSE;
1021
1022   SILC_LOG_DEBUG(("Marking client entry %p deleted", client_entry));
1023
1024   if (silc_atomic_sub_int32(&client_entry->internal.deleted, 1) != 0) {
1025     SILC_LOG_DEBUG(("Client entry %p already marked deleted", client_entry));
1026     return FALSE;
1027   }
1028
1029   silc_client_unref_client(client, conn, client_entry);
1030   return TRUE;
1031 }
1032
1033 /* Internal routine used to find client by ID and if not found this creates
1034    new client entry and returns it. */
1035
1036 SilcClientEntry silc_client_get_client(SilcClient client,
1037                                        SilcClientConnection conn,
1038                                        SilcClientID *client_id)
1039 {
1040   SilcClientEntry client_entry;
1041
1042   client_entry = silc_client_get_client_by_id(client, conn, client_id);
1043   if (!client_entry) {
1044     client_entry = silc_client_add_client(client, conn, NULL, NULL, NULL,
1045                                           client_id, 0);
1046     if (!client_entry)
1047       return NULL;
1048     silc_client_ref_client(client, conn, client_entry);
1049   }
1050
1051   return client_entry;
1052 }
1053
1054 /* Lock client */
1055
1056 void silc_client_lock_client(SilcClientEntry client_entry)
1057 {
1058   silc_rwlock_rdlock(client_entry->internal.lock);
1059 }
1060
1061 /* Unlock client */
1062
1063 void silc_client_unlock_client(SilcClientEntry client_entry)
1064 {
1065   silc_rwlock_unlock(client_entry->internal.lock);
1066 }
1067
1068 /* Take reference of client entry */
1069
1070 SilcClientEntry silc_client_ref_client(SilcClient client,
1071                                        SilcClientConnection conn,
1072                                        SilcClientEntry client_entry)
1073 {
1074   silc_atomic_add_int32(&client_entry->internal.refcnt, 1);
1075   SILC_LOG_DEBUG(("Client %p refcnt %d->%d", client_entry,
1076                   silc_atomic_get_int32(&client_entry->internal.refcnt) - 1,
1077                   silc_atomic_get_int32(&client_entry->internal.refcnt)));
1078   return client_entry;
1079 }
1080
1081 /* Release reference of client entry */
1082
1083 void silc_client_unref_client(SilcClient client, SilcClientConnection conn,
1084                               SilcClientEntry client_entry)
1085 {
1086   SilcBool ret;
1087
1088   if (!client_entry)
1089     return;
1090
1091   SILC_LOG_DEBUG(("Client %p refcnt %d->%d", client_entry,
1092                   silc_atomic_get_int32(&client_entry->internal.refcnt),
1093                   silc_atomic_get_int32(&client_entry->internal.refcnt) - 1));
1094
1095   if (silc_atomic_sub_int32(&client_entry->internal.refcnt, 1) > 0)
1096     return;
1097
1098   SILC_LOG_DEBUG(("Deleting client %p (%d)", client_entry,
1099                   silc_atomic_get_int32(&client_entry->internal.deleted)));
1100
1101   silc_mutex_lock(conn->internal->lock);
1102   ret = silc_idcache_del_by_context(conn->internal->client_cache,
1103                                     client_entry, NULL);
1104   silc_mutex_unlock(conn->internal->lock);
1105
1106   if (ret) {
1107     /* Remove from channels */
1108     silc_client_remove_from_channels(client, conn, client_entry);
1109
1110     /* Free the client entry data */
1111     silc_client_del_client_entry(client, conn, client_entry);
1112   }
1113 }
1114
1115 /* Free client entry list */
1116
1117 void silc_client_list_free(SilcClient client, SilcClientConnection conn,
1118                            SilcDList client_list)
1119 {
1120   SilcClientEntry client_entry;
1121
1122   if (client_list) {
1123     silc_dlist_start(client_list);
1124     while ((client_entry = silc_dlist_get(client_list)))
1125       silc_client_unref_client(client, conn, client_entry);
1126
1127     silc_dlist_uninit(client_list);
1128   }
1129 }
1130
1131 /* Formats the nickname of the client specified by the `client_entry'.
1132    If the format is specified by the application this will format the
1133    nickname and replace the old nickname in the client entry. If the
1134    format string is not specified then this function has no effect.
1135    Returns the client entry that was formatted. */
1136
1137 SilcClientEntry silc_client_nickname_format(SilcClient client,
1138                                             SilcClientConnection conn,
1139                                             SilcClientEntry client_entry,
1140                                             SilcBool priority)
1141 {
1142   char *cp;
1143   char newnick[128 + 1];
1144   int i, off = 0, len;
1145   SilcDList clients;
1146   SilcClientEntry entry, unformatted = NULL;
1147   SilcBool formatted = FALSE;
1148
1149   if (!client->internal->params->nickname_format[0])
1150     return client_entry;
1151   if (!client_entry->nickname[0])
1152     return NULL;
1153
1154   SILC_LOG_DEBUG(("Format nickname"));
1155
1156   /* Get all clients with same nickname. Do not perform the formatting
1157      if there aren't any clients with same nickname unless the application
1158      is forcing us to do so. */
1159   clients = silc_client_get_clients_local_ext(client, conn,
1160                                               client_entry->nickname,
1161                                               TRUE, FALSE);
1162   if (!clients)
1163     return NULL;
1164   if (silc_dlist_count(clients) == 1 && !priority &&
1165       !client->internal->params->nickname_force_format) {
1166     silc_client_list_free(client, conn, clients);
1167     return client_entry;
1168   }
1169
1170   /* Is the requested client formatted already */
1171   if (client_entry->nickname_normalized &&
1172       !silc_utf8_strcasecmp(client_entry->nickname,
1173                             client_entry->nickname_normalized))
1174     formatted = TRUE;
1175
1176   if (client->internal->params->nickname_force_format)
1177     formatted = FALSE;
1178
1179   /* Find unformatted client entry */
1180   while ((entry = silc_dlist_get(clients))) {
1181     if (!entry->internal.valid)
1182       continue;
1183     if (entry == client_entry)
1184       continue;
1185     if (silc_utf8_strcasecmp(entry->nickname, entry->nickname_normalized)) {
1186       unformatted = entry;
1187       break;
1188     }
1189   }
1190
1191   /* If there are no other unformatted clients and the requested client is
1192      unformatted, just return it. */
1193   if (!unformatted && !formatted) {
1194     silc_client_list_free(client, conn, clients);
1195     return client_entry;
1196   }
1197
1198   /* If priority formatting then the requested client will get the
1199      unformatted nickname, and the unformatted client will get a new
1200      formatted nickname. */
1201   if (priority) {
1202     if (formatted) {
1203       /* Simply change the client's nickname to unformatted */
1204       if (!silc_client_nickname_parse(client, conn, client_entry->nickname,
1205                                       &cp))
1206         return NULL;
1207
1208       silc_snprintf(client_entry->nickname, sizeof(client_entry->nickname),
1209                     cp);
1210       silc_free(cp);
1211     }
1212
1213     if (!unformatted) {
1214       /* There was no other unformatted client */
1215       silc_client_list_free(client, conn, clients);
1216       return client_entry;
1217     }
1218
1219     /* Now format the previously unformatted client */
1220     client_entry = unformatted;
1221     formatted = FALSE;
1222   }
1223
1224   /* If already formatted just return it */
1225   if (formatted) {
1226     silc_client_list_free(client, conn, clients);
1227     return client_entry;
1228   }
1229
1230   memset(newnick, 0, sizeof(newnick));
1231   cp = client->internal->params->nickname_format;
1232   while (cp && *cp) {
1233     if (*cp == '%') {
1234       cp++;
1235       continue;
1236     }
1237
1238     switch(*cp) {
1239     case 'n':
1240       /* Nickname */
1241       if (!client_entry->nickname[0])
1242         break;
1243       len = strlen(client_entry->nickname);
1244       memcpy(&newnick[off], client_entry->nickname, len);
1245       off += len;
1246       break;
1247     case 'h':
1248       /* Stripped hostname */
1249       if (!client_entry->hostname[0])
1250         break;
1251       len = strcspn(client_entry->hostname, ".");
1252       i = strcspn(client_entry->hostname, "-");
1253       if (i < len)
1254         len = i;
1255       memcpy(&newnick[off], client_entry->hostname, len);
1256       off += len;
1257       break;
1258     case 'H':
1259       /* Full hostname */
1260       if (!client_entry->hostname[0])
1261         break;
1262       len = strlen(client_entry->hostname);
1263       memcpy(&newnick[off], client_entry->hostname, len);
1264       off += len;
1265       break;
1266     case 'a':
1267       /* Ascending number */
1268       {
1269         char tmp[6];
1270         int num, max = 1;
1271
1272         if (silc_dlist_count(clients) == 1)
1273           break;
1274
1275         silc_dlist_start(clients);
1276         while ((entry = silc_dlist_get(clients))) {
1277           if (!silc_utf8_strncasecmp(entry->nickname, newnick, off))
1278             continue;
1279           if (strlen(entry->nickname) <= off)
1280             continue;
1281           num = atoi(&entry->nickname[off]);
1282           if (num > max)
1283             max = num;
1284         }
1285
1286         memset(tmp, 0, sizeof(tmp));
1287         silc_snprintf(tmp, sizeof(tmp) - 1, "%d", ++max);
1288         len = strlen(tmp);
1289         memcpy(&newnick[off], tmp, len);
1290         off += len;
1291       }
1292       break;
1293     default:
1294       /* Some other character in the string */
1295       memcpy(&newnick[off], cp, 1);
1296       off++;
1297       break;
1298     }
1299
1300     cp++;
1301   }
1302
1303   newnick[off] = 0;
1304   memset(client_entry->nickname, 0, sizeof(client_entry->nickname));
1305   memcpy(client_entry->nickname, newnick, strlen(newnick));
1306   silc_client_list_free(client, conn, clients);
1307
1308   return client_entry;
1309 }
1310
1311 /* Parses nickname according to nickname format string */
1312
1313 SilcBool silc_client_nickname_parse(SilcClient client,
1314                                     SilcClientConnection conn,
1315                                     char *nickname,
1316                                     char **ret_nick)
1317 {
1318   char *cp, s = 0, e = 0, *nick;
1319   SilcBool n = FALSE;
1320   int len;
1321
1322   if (!client->internal->params->nickname_format[0]) {
1323     *ret_nick = NULL;
1324     return TRUE;
1325   }
1326
1327   if (!nickname || !nickname[0])
1328     return FALSE;
1329
1330   cp = client->internal->params->nickname_format;
1331   while (cp && *cp) {
1332     if (*cp == '%') {
1333       cp++;
1334       continue;
1335     }
1336
1337     switch(*cp) {
1338     case 'n':
1339       n = TRUE;
1340       break;
1341
1342     case 'h':
1343     case 'H':
1344     case 'a':
1345       break;
1346
1347     default:
1348       /* Get separator character */
1349       if (n)
1350         e = *cp;
1351       else
1352         s = *cp;
1353       break;
1354     }
1355
1356     cp++;
1357   }
1358   if (!n)
1359     return FALSE;
1360
1361   /* Parse the nickname */
1362   nick = nickname;
1363   len = strlen(nick);
1364   if (s)
1365     if (strchr(nickname, s))
1366       nick = strchr(nickname, s) + 1;
1367   if (e)
1368     if (strchr(nick, e))
1369       len = strchr(nick, e) - nick;
1370   if (!len)
1371     return FALSE;
1372
1373   *ret_nick = silc_memdup(nick, len);
1374   if (!(*ret_nick))
1375     return FALSE;
1376
1377   SILC_LOG_DEBUG(("Parsed nickname: %s", *ret_nick));
1378
1379   return TRUE;
1380 }
1381
1382 /************************ Channel Searching Locally *************************/
1383
1384 /* Finds entry for channel by the channel name. Returns the entry or NULL
1385    if the entry was not found. It is found only if the client is joined
1386    to the channel. */
1387
1388 SilcChannelEntry silc_client_get_channel(SilcClient client,
1389                                          SilcClientConnection conn,
1390                                          char *channel)
1391 {
1392   SilcList list;
1393   SilcIDCacheEntry id_cache;
1394   SilcChannelEntry entry = NULL;
1395   char chname[256 + 1], server[256 + 1];
1396
1397   if (!client || !conn || !channel)
1398     return NULL;
1399
1400   SILC_LOG_DEBUG(("Find channel %s", channel));
1401
1402   /* Parse server name from channel name */
1403   silc_parse_userfqdn(channel, chname, sizeof(chname), server, sizeof(server));
1404
1405   /* Normalize name for search */
1406   channel = silc_channel_name_check(chname, strlen(chname), SILC_STRING_UTF8,
1407                                     256, NULL);
1408   if (!channel)
1409     return NULL;
1410
1411   silc_mutex_lock(conn->internal->lock);
1412
1413   if (!silc_idcache_find_by_name(conn->internal->channel_cache, channel,
1414                                  &list)) {
1415     silc_mutex_unlock(conn->internal->lock);
1416     silc_free(channel);
1417     return NULL;
1418   }
1419
1420   /* If server name was specified with channel name, find the correct
1421      channel entry with the server name.  There can only be one channel
1422      with same name on same server. */
1423   silc_list_start(list);
1424   if (server[0]) {
1425     while ((id_cache = silc_list_get(list))) {
1426       entry = id_cache->context;
1427       if (!entry->server[0])
1428         continue;
1429       if (silc_utf8_strcasecmp(entry->server, server))
1430         break;
1431     }
1432   } else {
1433     /* Get first channel without server name specified or one with our
1434        current server connection name */
1435     while ((id_cache = silc_list_get(list))) {
1436       entry = id_cache->context;
1437       if (!entry->server[0])
1438         break;
1439       if (silc_utf8_strcasecmp(entry->server, conn->remote_host))
1440         break;
1441     }
1442   }
1443
1444   if (!id_cache) {
1445     silc_mutex_unlock(conn->internal->lock);
1446     silc_free(channel);
1447     return NULL;
1448   }
1449
1450   SILC_LOG_DEBUG(("Found channel %s%s%s", entry->channel_name,
1451                   entry->server[0] ? "@" : "", entry->server));
1452
1453   /* Reference */
1454   silc_client_ref_channel(client, conn, entry);
1455   silc_mutex_unlock(conn->internal->lock);
1456
1457   silc_free(channel);
1458
1459   return entry;
1460 }
1461
1462 /* Finds entry for channel by the channel ID. Returns the entry or NULL
1463    if the entry was not found. It is found only if the client is joined
1464    to the channel. */
1465
1466 SilcChannelEntry silc_client_get_channel_by_id(SilcClient client,
1467                                                SilcClientConnection conn,
1468                                                SilcChannelID *channel_id)
1469 {
1470   SilcIDCacheEntry id_cache;
1471   SilcChannelEntry entry;
1472
1473   if (!client || !conn || !channel_id)
1474     return NULL;
1475
1476   SILC_LOG_DEBUG(("Find channel by id %s",
1477                   silc_id_render(channel_id, SILC_ID_CHANNEL)));
1478
1479   silc_mutex_lock(conn->internal->lock);
1480
1481   if (!silc_idcache_find_by_id_one(conn->internal->channel_cache, channel_id,
1482                                    &id_cache)) {
1483     silc_mutex_unlock(conn->internal->lock);
1484     return NULL;
1485   }
1486
1487   SILC_LOG_DEBUG(("Found"));
1488
1489   entry = id_cache->context;
1490
1491   /* Reference */
1492   silc_client_ref_channel(client, conn, entry);
1493   silc_mutex_unlock(conn->internal->lock);
1494
1495   return entry;
1496 }
1497
1498 /********************** Channel Resolving from Server ***********************/
1499
1500 /* Channel resolving context */
1501 typedef struct {
1502   SilcDList channels;
1503   SilcGetChannelCallback completion;
1504   void *context;
1505 } *SilcClientGetChannelInternal;
1506
1507 /* Resolving command callback */
1508
1509 static SilcBool silc_client_get_channel_cb(SilcClient client,
1510                                            SilcClientConnection conn,
1511                                            SilcCommand command,
1512                                            SilcStatus status,
1513                                            SilcStatus error,
1514                                            void *context,
1515                                            va_list ap)
1516 {
1517   SilcClientGetChannelInternal i = context;
1518   SilcChannelEntry entry;
1519
1520   if (error != SILC_STATUS_OK) {
1521     SILC_LOG_DEBUG(("Resolving failed: %s", silc_get_status_message(error)));
1522     if (i->completion)
1523       i->completion(client, conn, error, NULL, i->context);
1524     goto out;
1525   }
1526
1527   /* Add the returned channel to list */
1528   if (i->completion) {
1529     entry = va_arg(ap, SilcChannelEntry);
1530     silc_client_ref_channel(client, conn, entry);
1531     silc_dlist_add(i->channels, entry);
1532   }
1533
1534   if (status == SILC_STATUS_OK || status == SILC_STATUS_LIST_END) {
1535     /* Deliver the channels to the caller */
1536     if (i->completion) {
1537       SILC_LOG_DEBUG(("Resolved %d channels", silc_dlist_count(i->channels)));
1538       silc_dlist_start(i->channels);
1539       i->completion(client, conn, SILC_STATUS_OK, i->channels, i->context);
1540     }
1541     goto out;
1542   }
1543
1544   return TRUE;
1545
1546  out:
1547   silc_client_list_free_channels(client, conn, i->channels);
1548   silc_free(i);
1549   return FALSE;
1550 }
1551
1552 /* Resolves channel entry from the server by the channel name. */
1553
1554 void silc_client_get_channel_resolve(SilcClient client,
1555                                      SilcClientConnection conn,
1556                                      char *channel_name,
1557                                      SilcGetChannelCallback completion,
1558                                      void *context)
1559 {
1560   SilcClientGetChannelInternal i;
1561
1562   if (!client || !conn || !channel_name || !completion)
1563     return;
1564
1565   SILC_LOG_DEBUG(("Resolve channel %s", channel_name));
1566
1567   i = silc_calloc(1, sizeof(*i));
1568   if (!i)
1569     return;
1570   i->completion = completion;
1571   i->context = context;
1572   i->channels = silc_dlist_init();
1573   if (!i->channels) {
1574     silc_free(i);
1575     return;
1576   }
1577
1578   /* Send the command */
1579   if (!silc_client_command_send(client, conn, SILC_COMMAND_IDENTIFY,
1580                                 silc_client_get_channel_cb, i, 1,
1581                                 3, channel_name, strlen(channel_name))) {
1582     if (completion)
1583       completion(client, conn, SILC_STATUS_ERR_RESOURCE_LIMIT, NULL, context);
1584   }
1585 }
1586
1587 /* Resolves channel information from the server by the channel ID. */
1588
1589 SilcUInt16
1590 silc_client_get_channel_by_id_resolve(SilcClient client,
1591                                       SilcClientConnection conn,
1592                                       SilcChannelID *channel_id,
1593                                       SilcGetChannelCallback completion,
1594                                       void *context)
1595 {
1596   SilcClientGetChannelInternal i;
1597   SilcBuffer idp;
1598   SilcUInt16 cmd_ident;
1599
1600   if (!client || !conn || !channel_id || !completion)
1601     return 0;
1602
1603   SILC_LOG_DEBUG(("Resolve channel by id %s",
1604                   silc_id_render(channel_id, SILC_ID_CHANNEL)));
1605
1606   i = silc_calloc(1, sizeof(*i));
1607   if (!i)
1608     return 0;
1609   i->completion = completion;
1610   i->context = context;
1611   i->channels = silc_dlist_init();
1612   if (!i->channels) {
1613     silc_free(i);
1614     return 0;
1615   }
1616
1617   /* Send the command */
1618   idp = silc_id_payload_encode(channel_id, SILC_ID_CHANNEL);
1619   cmd_ident = silc_client_command_send(client, conn, SILC_COMMAND_IDENTIFY,
1620                                        silc_client_get_channel_cb, i, 1,
1621                                        5, silc_buffer_datalen(idp));
1622   silc_buffer_free(idp);
1623   if (!cmd_ident && completion)
1624     completion(client, conn, SILC_STATUS_ERR_RESOURCE_LIMIT, NULL, context);
1625
1626   return cmd_ident;
1627 }
1628
1629 /************************* Channel Entry Routines ***************************/
1630
1631 /* Add new channel entry to the ID Cache */
1632
1633 SilcChannelEntry silc_client_add_channel(SilcClient client,
1634                                          SilcClientConnection conn,
1635                                          const char *channel_name,
1636                                          SilcUInt32 mode,
1637                                          SilcChannelID *channel_id)
1638 {
1639   SilcChannelEntry channel;
1640   char *channel_namec, name[256 + 1];
1641
1642   SILC_LOG_DEBUG(("Adding channel %s", channel_name));
1643
1644   channel = silc_calloc(1, sizeof(*channel));
1645   if (!channel)
1646     return NULL;
1647
1648   silc_rwlock_alloc(&channel->internal.lock);
1649   silc_atomic_init32(&channel->internal.refcnt, 0);
1650   silc_atomic_init32(&channel->internal.deleted, 1);
1651   channel->id = *channel_id;
1652   channel->mode = mode;
1653
1654   silc_parse_userfqdn(channel_name, name, sizeof(name),
1655                       channel->server, sizeof(channel->server));
1656   if (client->internal->params->full_channel_names)
1657     channel->channel_name = strdup(channel_name);
1658   else
1659     channel->channel_name = strdup(name);
1660
1661   if (!channel->channel_name) {
1662     silc_rwlock_free(channel->internal.lock);
1663     silc_atomic_uninit32(&channel->internal.refcnt);
1664     silc_atomic_uninit32(&channel->internal.deleted);
1665     silc_free(channel);
1666     return NULL;
1667   }
1668
1669   channel->user_list = silc_hash_table_alloc(1, silc_hash_ptr, NULL, NULL,
1670                                              NULL, NULL, NULL, TRUE);
1671   if (!channel->user_list) {
1672     silc_rwlock_free(channel->internal.lock);
1673     silc_atomic_uninit32(&channel->internal.refcnt);
1674     silc_atomic_uninit32(&channel->internal.deleted);
1675     silc_free(channel->channel_name);
1676     silc_free(channel);
1677     return NULL;
1678   }
1679
1680   /* Normalize channel name */
1681   channel_namec = silc_channel_name_check(name, strlen(name),
1682                                           SILC_STRING_UTF8, 256, NULL);
1683   if (!channel_namec) {
1684     silc_rwlock_free(channel->internal.lock);
1685     silc_atomic_uninit32(&channel->internal.refcnt);
1686     silc_atomic_uninit32(&channel->internal.deleted);
1687     silc_free(channel->channel_name);
1688     silc_hash_table_free(channel->user_list);
1689     silc_free(channel);
1690     return NULL;
1691   }
1692
1693   silc_mutex_lock(conn->internal->lock);
1694
1695   /* Add channel to cache, the normalized channel name is saved to cache */
1696   if (!silc_idcache_add(conn->internal->channel_cache, channel_namec,
1697                         &channel->id, channel)) {
1698     silc_rwlock_free(channel->internal.lock);
1699     silc_atomic_uninit32(&channel->internal.refcnt);
1700     silc_atomic_uninit32(&channel->internal.deleted);
1701     silc_free(channel_namec);
1702     silc_free(channel->channel_name);
1703     silc_hash_table_free(channel->user_list);
1704     silc_free(channel);
1705     silc_mutex_unlock(conn->internal->lock);
1706     return NULL;
1707   }
1708
1709   silc_mutex_unlock(conn->internal->lock);
1710   silc_client_ref_channel(client, conn, channel);
1711
1712   SILC_LOG_DEBUG(("Added %p", channel));
1713
1714   return channel;
1715 }
1716
1717 /* Removes channel from the cache by the channel entry. */
1718
1719 SilcBool silc_client_del_channel(SilcClient client, SilcClientConnection conn,
1720                                  SilcChannelEntry channel)
1721 {
1722   if (!channel)
1723     return FALSE;
1724
1725   SILC_LOG_DEBUG(("Marking channel entry %p deleted", channel));
1726
1727   if (silc_atomic_sub_int32(&channel->internal.deleted, 1) != 0) {
1728     SILC_LOG_DEBUG(("Channel entry %p already marked deleted", channel));
1729     return FALSE;
1730   }
1731
1732   silc_client_unref_channel(client, conn, channel);
1733   return TRUE;
1734 }
1735
1736 /* Replaces the channel ID of the `channel' to `new_id'. Returns FALSE
1737    if the ID could not be changed.  This handles entry locking internally. */
1738
1739 SilcBool silc_client_replace_channel_id(SilcClient client,
1740                                         SilcClientConnection conn,
1741                                         SilcChannelEntry channel,
1742                                         SilcChannelID *new_id)
1743 {
1744   SilcBool ret = FALSE;
1745
1746   if (!new_id)
1747     return FALSE;
1748
1749   SILC_LOG_DEBUG(("Old Channel ID id(%s)",
1750                   silc_id_render(&channel->id, SILC_ID_CHANNEL)));
1751   SILC_LOG_DEBUG(("New Channel ID id(%s)",
1752                   silc_id_render(new_id, SILC_ID_CHANNEL)));
1753
1754   /* Update the ID */
1755   silc_rwlock_wrlock(channel->internal.lock);
1756   silc_mutex_lock(conn->internal->lock);
1757   silc_idcache_update_by_context(conn->internal->channel_cache, channel,
1758                                  new_id, NULL, FALSE);
1759   silc_mutex_unlock(conn->internal->lock);
1760   silc_rwlock_unlock(channel->internal.lock);
1761
1762   return ret;
1763 }
1764
1765 /* Lock channel */
1766
1767 void silc_client_lock_channel(SilcChannelEntry channel_entry)
1768 {
1769   silc_rwlock_rdlock(channel_entry->internal.lock);
1770 }
1771
1772 /* Unlock channel */
1773
1774 void silc_client_unlock_channel(SilcChannelEntry channel_entry)
1775 {
1776   silc_rwlock_unlock(channel_entry->internal.lock);
1777 }
1778
1779 /* Take reference of channel entry */
1780
1781 SilcChannelEntry silc_client_ref_channel(SilcClient client,
1782                                          SilcClientConnection conn,
1783                                          SilcChannelEntry channel_entry)
1784 {
1785   silc_atomic_add_int32(&channel_entry->internal.refcnt, 1);
1786   SILC_LOG_DEBUG(("Channel %p refcnt %d->%d", channel_entry,
1787                   silc_atomic_get_int32(&channel_entry->internal.refcnt) - 1,
1788                   silc_atomic_get_int32(&channel_entry->internal.refcnt)));
1789   return channel_entry;
1790 }
1791
1792 /* Release reference of channel entry */
1793
1794 void silc_client_unref_channel(SilcClient client, SilcClientConnection conn,
1795                                SilcChannelEntry channel_entry)
1796 {
1797   SilcIDCacheEntry id_cache;
1798   SilcBool ret = TRUE;
1799   SilcCipher key;
1800   SilcHmac hmac;
1801   char *namec;
1802
1803   if (!channel_entry)
1804     return;
1805
1806   SILC_LOG_DEBUG(("Channel %p refcnt %d->%d", channel_entry,
1807                   silc_atomic_get_int32(&channel_entry->internal.refcnt),
1808                   silc_atomic_get_int32(&channel_entry->internal.refcnt)
1809                   - 1));
1810
1811   if (silc_atomic_sub_int32(&channel_entry->internal.refcnt, 1) > 0)
1812     return;
1813
1814   SILC_LOG_DEBUG(("Deleting channel %p", channel_entry));
1815
1816   silc_mutex_lock(conn->internal->lock);
1817   if (silc_idcache_find_by_context(conn->internal->channel_cache, channel_entry,
1818                                    &id_cache)) {
1819     namec = id_cache->name;
1820     ret = silc_idcache_del_by_context(conn->internal->channel_cache,
1821                                       channel_entry, NULL);
1822     silc_free(namec);
1823   }
1824   silc_mutex_unlock(conn->internal->lock);
1825
1826   if (!ret)
1827     return;
1828
1829   silc_client_empty_channel(client, conn, channel_entry);
1830   silc_client_del_channel_private_keys(client, conn, channel_entry);
1831   silc_hash_table_free(channel_entry->user_list);
1832   silc_free(channel_entry->channel_name);
1833   silc_free(channel_entry->topic);
1834   if (channel_entry->founder_key)
1835     silc_pkcs_public_key_free(channel_entry->founder_key);
1836   if (channel_entry->internal.send_key)
1837     silc_cipher_free(channel_entry->internal.send_key);
1838   if (channel_entry->internal.receive_key)
1839     silc_cipher_free(channel_entry->internal.receive_key);
1840   if (channel_entry->internal.hmac)
1841     silc_hmac_free(channel_entry->internal.hmac);
1842   if (channel_entry->internal.old_channel_keys) {
1843     silc_dlist_start(channel_entry->internal.old_channel_keys);
1844     while ((key = silc_dlist_get(channel_entry->internal.old_channel_keys)))
1845       silc_cipher_free(key);
1846     silc_dlist_uninit(channel_entry->internal.old_channel_keys);
1847   }
1848   if (channel_entry->internal.old_hmacs) {
1849     silc_dlist_start(channel_entry->internal.old_hmacs);
1850     while ((hmac = silc_dlist_get(channel_entry->internal.old_hmacs)))
1851       silc_hmac_free(hmac);
1852     silc_dlist_uninit(channel_entry->internal.old_hmacs);
1853   }
1854   if (channel_entry->channel_pubkeys)
1855     silc_argument_list_free(channel_entry->channel_pubkeys,
1856                             SILC_ARGUMENT_PUBLIC_KEY);
1857   silc_atomic_uninit32(&channel_entry->internal.deleted);
1858   silc_atomic_uninit32(&channel_entry->internal.refcnt);
1859   silc_rwlock_free(channel_entry->internal.lock);
1860   silc_schedule_task_del_by_context(conn->client->schedule, channel_entry);
1861   silc_free(channel_entry);
1862 }
1863
1864 /* Free channel entry list */
1865
1866 void silc_client_list_free_channels(SilcClient client,
1867                                     SilcClientConnection conn,
1868                                     SilcDList channel_list)
1869 {
1870   SilcChannelEntry channel_entry;
1871
1872   if (channel_list) {
1873     silc_dlist_start(channel_list);
1874     while ((channel_entry = silc_dlist_get(channel_list)))
1875       silc_client_unref_channel(client, conn, channel_entry);
1876
1877     silc_dlist_uninit(channel_list);
1878   }
1879 }
1880
1881 /************************* Server Searching Locally *************************/
1882
1883 /* Finds entry for server by the server name. */
1884
1885 SilcServerEntry silc_client_get_server(SilcClient client,
1886                                        SilcClientConnection conn,
1887                                        char *server_name)
1888 {
1889   SilcIDCacheEntry id_cache;
1890   SilcServerEntry entry;
1891
1892   if (!client || !conn || !server_name)
1893     return NULL;
1894
1895   SILC_LOG_DEBUG(("Find server by name %s", server_name));
1896
1897   /* Normalize server name for search */
1898   server_name = silc_identifier_check(server_name, strlen(server_name),
1899                                       SILC_STRING_UTF8, 256, NULL);
1900   if (!server_name)
1901     return NULL;
1902
1903   silc_mutex_lock(conn->internal->lock);
1904
1905   if (!silc_idcache_find_by_name_one(conn->internal->server_cache,
1906                                      server_name, &id_cache)) {
1907     silc_free(server_name);
1908     silc_mutex_unlock(conn->internal->lock);
1909     return NULL;
1910   }
1911
1912   SILC_LOG_DEBUG(("Found"));
1913
1914   /* Reference */
1915   entry = id_cache->context;
1916   silc_client_ref_server(client, conn, entry);
1917
1918   silc_mutex_unlock(conn->internal->lock);
1919
1920   silc_free(server_name);
1921
1922   return entry;
1923 }
1924
1925 /* Finds entry for server by the server ID. */
1926
1927 SilcServerEntry silc_client_get_server_by_id(SilcClient client,
1928                                              SilcClientConnection conn,
1929                                              SilcServerID *server_id)
1930 {
1931   SilcIDCacheEntry id_cache;
1932   SilcServerEntry entry;
1933
1934   if (!client || !conn || !server_id)
1935     return NULL;
1936
1937   SILC_LOG_DEBUG(("Find server by id %s",
1938                   silc_id_render(server_id, SILC_ID_SERVER)));
1939
1940   silc_mutex_lock(conn->internal->lock);
1941
1942   if (!silc_idcache_find_by_id_one(conn->internal->server_cache,
1943                                    server_id, &id_cache)) {
1944     silc_mutex_unlock(conn->internal->lock);
1945     return NULL;
1946   }
1947
1948   SILC_LOG_DEBUG(("Found"));
1949
1950   /* Reference */
1951   entry = id_cache->context;
1952   silc_client_ref_server(client, conn, entry);
1953
1954   silc_mutex_unlock(conn->internal->lock);
1955
1956   return entry;
1957 }
1958
1959 /*********************** Server Resolving from Server ***********************/
1960
1961 /* Resolving context */
1962 typedef struct {
1963   SilcDList servers;
1964   SilcGetServerCallback completion;
1965   void *context;
1966 } *SilcClientGetServerInternal;
1967
1968 /* Resolving command callback */
1969
1970 static SilcBool silc_client_get_server_cb(SilcClient client,
1971                                           SilcClientConnection conn,
1972                                           SilcCommand command,
1973                                           SilcStatus status,
1974                                           SilcStatus error,
1975                                           void *context,
1976                                           va_list ap)
1977 {
1978   SilcClientGetServerInternal i = context;
1979   SilcServerEntry server;
1980
1981   if (error != SILC_STATUS_OK) {
1982     SILC_LOG_DEBUG(("Resolving failed: %s", silc_get_status_message(error)));
1983     if (i->completion)
1984       i->completion(client, conn, error, NULL, i->context);
1985     goto out;
1986   }
1987
1988   /* Add the returned servers to list */
1989   if (i->completion) {
1990     server = va_arg(ap, SilcServerEntry);
1991     silc_client_ref_server(client, conn, server);
1992     silc_dlist_add(i->servers, server);
1993     server->internal.resolve_cmd_ident = 0;
1994   }
1995
1996   if (status == SILC_STATUS_OK || status == SILC_STATUS_LIST_END) {
1997     /* Deliver the servers to the caller */
1998     if (i->completion) {
1999       SILC_LOG_DEBUG(("Resolved %d servers", silc_dlist_count(i->servers)));
2000       silc_dlist_start(i->servers);
2001       i->completion(client, conn, SILC_STATUS_OK, i->servers, i->context);
2002     }
2003     goto out;
2004   }
2005
2006   return TRUE;
2007
2008  out:
2009   silc_client_list_free_servers(client, conn, i->servers);
2010   silc_free(i);
2011   return FALSE;
2012 }
2013
2014 /* Resolve server by server ID */
2015
2016 SilcUInt16
2017 silc_client_get_server_by_id_resolve(SilcClient client,
2018                                      SilcClientConnection conn,
2019                                      SilcServerID *server_id,
2020                                      SilcGetServerCallback completion,
2021                                      void *context)
2022 {
2023   SilcClientGetServerInternal i;
2024   SilcServerEntry server;
2025   SilcBuffer idp;
2026   SilcUInt16 cmd_ident;
2027
2028   if (!client || !conn || !server_id || !completion)
2029     return 0;
2030
2031   SILC_LOG_DEBUG(("Resolve server by id %s",
2032                   silc_id_render(server_id, SILC_ID_SERVER)));
2033
2034   i = silc_calloc(1, sizeof(*i));
2035   if (!i)
2036     return 0;
2037   i->completion = completion;
2038   i->context = context;
2039   i->servers = silc_dlist_init();
2040   if (!i->servers) {
2041     silc_free(i);
2042     return 0;
2043   }
2044
2045   /* Attach to resolving, if on going */
2046   server = silc_client_get_server_by_id(client, conn, server_id);
2047   if (server && server->internal.resolve_cmd_ident) {
2048     SILC_LOG_DEBUG(("Attach to existing resolving"));
2049     silc_client_unref_server(client, conn, server);
2050     silc_client_command_pending(conn, SILC_COMMAND_NONE,
2051                                 server->internal.resolve_cmd_ident,
2052                                 silc_client_get_server_cb, i);
2053     return server->internal.resolve_cmd_ident;
2054   }
2055
2056   /* Send the command */
2057   idp = silc_id_payload_encode(server_id, SILC_ID_SERVER);
2058   cmd_ident = silc_client_command_send(client, conn, SILC_COMMAND_IDENTIFY,
2059                                        silc_client_get_server_cb, i, 1,
2060                                        5, silc_buffer_datalen(idp));
2061   silc_buffer_free(idp);
2062   if (!cmd_ident && completion)
2063     completion(client, conn, SILC_STATUS_ERR_RESOURCE_LIMIT, NULL, context);
2064
2065   if (server && cmd_ident)
2066     server->internal.resolve_cmd_ident = cmd_ident;
2067
2068   silc_client_unref_server(client, conn, server);
2069
2070   return cmd_ident;
2071 }
2072
2073 /************************** Server Entry Routines ***************************/
2074
2075 /* Add new server entry */
2076
2077 SilcServerEntry silc_client_add_server(SilcClient client,
2078                                        SilcClientConnection conn,
2079                                        const char *server_name,
2080                                        const char *server_info,
2081                                        SilcServerID *server_id)
2082 {
2083   SilcServerEntry server_entry;
2084   char *server_namec = NULL;
2085
2086   if (!server_id)
2087     return NULL;
2088
2089   SILC_LOG_DEBUG(("Adding new server %s", server_name));
2090
2091   server_entry = silc_calloc(1, sizeof(*server_entry));
2092   if (!server_entry)
2093     return NULL;
2094
2095   silc_rwlock_alloc(&server_entry->internal.lock);
2096   silc_atomic_init32(&server_entry->internal.refcnt, 0);
2097   silc_atomic_init32(&server_entry->internal.deleted, 1);
2098   server_entry->id = *server_id;
2099   if (server_name)
2100     server_entry->server_name = strdup(server_name);
2101   if (server_info)
2102     server_entry->server_info = strdup(server_info);
2103
2104   /* Normalize server name */
2105   if (server_name) {
2106     server_namec = silc_identifier_check(server_name, strlen(server_name),
2107                                          SILC_STRING_UTF8, 256, NULL);
2108     if (!server_namec) {
2109       silc_free(server_entry->server_name);
2110       silc_free(server_entry->server_info);
2111       silc_atomic_uninit32(&server_entry->internal.deleted);
2112       silc_atomic_uninit32(&server_entry->internal.refcnt);
2113       silc_rwlock_free(server_entry->internal.lock);
2114       silc_free(server_entry);
2115       return NULL;
2116     }
2117   }
2118
2119   silc_mutex_lock(conn->internal->lock);
2120
2121   /* Add server to cache */
2122   if (!silc_idcache_add(conn->internal->server_cache, server_namec,
2123                         &server_entry->id, server_entry)) {
2124     silc_free(server_namec);
2125     silc_free(server_entry->server_name);
2126     silc_free(server_entry->server_info);
2127     silc_atomic_uninit32(&server_entry->internal.deleted);
2128     silc_atomic_uninit32(&server_entry->internal.refcnt);
2129     silc_rwlock_free(server_entry->internal.lock);
2130     silc_free(server_entry);
2131     silc_mutex_unlock(conn->internal->lock);
2132     return NULL;
2133   }
2134
2135   silc_mutex_unlock(conn->internal->lock);
2136   silc_client_ref_server(client, conn, server_entry);
2137
2138   SILC_LOG_DEBUG(("Added %p", server_entry));
2139
2140   return server_entry;
2141 }
2142
2143 /* Removes server from the cache by the server entry. */
2144
2145 SilcBool silc_client_del_server(SilcClient client, SilcClientConnection conn,
2146                                 SilcServerEntry server)
2147 {
2148   if (!server)
2149     return FALSE;
2150
2151   if (silc_atomic_sub_int32(&server->internal.deleted, 1) != 0)
2152     return FALSE;
2153
2154   silc_client_unref_server(client, conn, server);
2155   return TRUE;
2156 }
2157
2158 /* Updates the `server_entry' with the new information sent as argument. */
2159
2160 void silc_client_update_server(SilcClient client,
2161                                SilcClientConnection conn,
2162                                SilcServerEntry server_entry,
2163                                const char *server_name,
2164                                const char *server_info)
2165 {
2166   char *server_namec = NULL;
2167
2168   SILC_LOG_DEBUG(("Updating server %p", server_entry));
2169
2170   if (server_name &&
2171       (!server_entry->server_name ||
2172        !silc_utf8_strcasecmp(server_entry->server_name, server_name))) {
2173
2174     server_namec = silc_identifier_check(server_name, strlen(server_name),
2175                                          SILC_STRING_UTF8, 256, NULL);
2176     if (!server_namec)
2177       return;
2178
2179     silc_free(server_entry->server_name);
2180     server_entry->server_name = strdup(server_name);
2181     if (!server_entry->server_name)
2182       return;
2183
2184     /* Update cache entry */
2185     silc_mutex_lock(conn->internal->lock);
2186     silc_idcache_update_by_context(conn->internal->server_cache, server_entry,
2187                                    NULL, server_namec, TRUE);
2188     silc_mutex_unlock(conn->internal->lock);
2189   }
2190
2191   if (server_info &&
2192       (!server_entry->server_info ||
2193        !silc_utf8_strcasecmp(server_entry->server_info, server_info))) {
2194     silc_free(server_entry->server_info);
2195     server_entry->server_info = strdup(server_info);
2196   }
2197 }
2198
2199 /* Lock server */
2200
2201 void silc_client_lock_server(SilcServerEntry server_entry)
2202 {
2203   silc_rwlock_rdlock(server_entry->internal.lock);
2204 }
2205
2206 /* Unlock server */
2207
2208 void silc_client_unlock_server(SilcServerEntry server_entry)
2209 {
2210   silc_rwlock_unlock(server_entry->internal.lock);
2211 }
2212
2213 /* Take reference of server entry */
2214
2215 SilcServerEntry silc_client_ref_server(SilcClient client,
2216                                        SilcClientConnection conn,
2217                                        SilcServerEntry server_entry)
2218 {
2219   silc_atomic_add_int32(&server_entry->internal.refcnt, 1);
2220   SILC_LOG_DEBUG(("Server %p refcnt %d->%d", server_entry,
2221                   silc_atomic_get_int32(&server_entry->internal.refcnt) - 1,
2222                   silc_atomic_get_int32(&server_entry->internal.refcnt)));
2223   return server_entry;
2224 }
2225
2226 /* Release reference of server entry */
2227
2228 void silc_client_unref_server(SilcClient client, SilcClientConnection conn,
2229                               SilcServerEntry server_entry)
2230 {
2231   SilcBool ret;
2232   SilcIDCacheEntry id_cache;
2233   char *namec;
2234
2235   if (!server_entry)
2236     return;
2237
2238   if (silc_atomic_sub_int32(&server_entry->internal.refcnt, 1) > 0)
2239     return;
2240
2241   SILC_LOG_DEBUG(("Deleting server %p", server_entry));
2242
2243   silc_mutex_lock(conn->internal->lock);
2244   if (silc_idcache_find_by_context(conn->internal->server_cache, server_entry,
2245                                    &id_cache)) {
2246     namec = id_cache->name;
2247     ret = silc_idcache_del_by_context(conn->internal->server_cache,
2248                                       server_entry, NULL);
2249     silc_free(namec);
2250   }
2251   silc_mutex_unlock(conn->internal->lock);
2252
2253   silc_free(server_entry->server_name);
2254   silc_free(server_entry->server_info);
2255   if (server_entry->public_key)
2256     silc_pkcs_public_key_free(server_entry->public_key);
2257   silc_atomic_uninit32(&server_entry->internal.deleted);
2258   silc_atomic_uninit32(&server_entry->internal.refcnt);
2259   silc_rwlock_free(server_entry->internal.lock);
2260   silc_free(server_entry);
2261 }
2262
2263 /* Free server entry list */
2264
2265 void silc_client_list_free_servers(SilcClient client,
2266                                    SilcClientConnection conn,
2267                                    SilcDList server_list)
2268 {
2269   SilcServerEntry server_entry;
2270
2271   if (server_list) {
2272     silc_dlist_start(server_list);
2273     while ((server_entry = silc_dlist_get(server_list)))
2274       silc_client_unref_server(client, conn, server_entry);
2275
2276     silc_dlist_uninit(server_list);
2277   }
2278 }