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