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