Handle command reply lists in threads.
[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 - 2006 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(SilcClient client,
66                                         SilcClientConnection conn,
67                                         const char *nickname,
68                                         const char *format)
69 {
70   SilcIDCacheEntry id_cache;
71   SilcList list;
72   SilcDList clients;
73   SilcClientEntry entry;
74   char *nicknamec;
75
76   if (!client || !conn || !nickname)
77     return NULL;
78
79   SILC_LOG_DEBUG(("Find clients by nickname %s", nickname));
80
81   /* Normalize nickname for search */
82   nicknamec = silc_identifier_check(nickname, strlen(nickname),
83                                     SILC_STRING_UTF8, 128, NULL);
84   if (!nicknamec)
85     return NULL;
86
87   clients = silc_dlist_init();
88   if (!clients) {
89     silc_free(nicknamec);
90     return NULL;
91   }
92
93   silc_mutex_lock(conn->internal->lock);
94
95   /* Find from cache */
96   silc_list_init(list, struct SilcIDCacheEntryStruct, next);
97   if (!silc_idcache_find_by_name(conn->internal->client_cache, nicknamec,
98                                  &list)) {
99     silc_mutex_unlock(conn->internal->lock);
100     silc_free(nicknamec);
101     silc_dlist_uninit(clients);
102     return NULL;
103   }
104
105   if (!format) {
106     /* Take all without any further checking */
107     silc_list_start(list);
108     while ((id_cache = silc_list_get(list))) {
109       silc_client_ref_client(client, conn, id_cache->context);
110       silc_dlist_add(clients, id_cache->context);
111     }
112   } else {
113     /* Check multiple cache entries for exact match */
114     silc_list_start(list);
115     while ((id_cache = silc_list_get(list))) {
116       entry = id_cache->context;
117       if (silc_utf8_strcasecmp(entry->nickname, format)) {
118         silc_client_ref_client(client, conn, entry);
119         silc_dlist_add(clients, entry);
120       }
121     }
122   }
123
124   silc_mutex_unlock(conn->internal->lock);
125
126   silc_dlist_start(clients);
127
128   silc_free(nicknamec);
129   return clients;
130 }
131
132 /********************** Client Resolving from Server ************************/
133
134 /* Resolving context */
135 typedef struct {
136   SilcDList clients;
137   SilcGetClientCallback completion;
138   void *context;
139 } *SilcClientGetClientInternal;
140
141 /* Resolving command callback */
142
143 static SilcBool silc_client_get_clients_cb(SilcClient client,
144                                            SilcClientConnection conn,
145                                            SilcCommand command,
146                                            SilcStatus status,
147                                            SilcStatus error,
148                                            void *context,
149                                            va_list ap)
150 {
151   SilcClientGetClientInternal i = context;
152   SilcClientEntry client_entry;
153
154   if (error != SILC_STATUS_OK) {
155     SILC_LOG_DEBUG(("Resolving failed: %s", silc_get_status_message(error)));
156     if (i->completion)
157       i->completion(client, conn, error, NULL, i->context);
158     goto out;
159   }
160
161   /* Add the returned client to list */
162   if (i->completion) {
163     client_entry = va_arg(ap, SilcClientEntry);
164     silc_client_ref_client(client, conn, client_entry);
165     silc_dlist_add(i->clients, client_entry);
166     client_entry->internal.resolve_cmd_ident = 0;
167   }
168
169   if (status == SILC_STATUS_OK || status == SILC_STATUS_LIST_END) {
170     /* Deliver the clients to the caller */
171     if (i->completion) {
172       SILC_LOG_DEBUG(("Resolved %d clients", silc_dlist_count(i->clients)));
173       silc_dlist_start(i->clients);
174       i->completion(client, conn, SILC_STATUS_OK, i->clients, i->context);
175     }
176     goto out;
177   }
178
179   return TRUE;
180
181  out:
182   silc_client_list_free(client, conn, i->clients);
183   silc_free(i);
184   return FALSE;
185 }
186
187 /* Resolves client information from server by the client ID. */
188
189 SilcUInt16
190 silc_client_get_client_by_id_resolve(SilcClient client,
191                                      SilcClientConnection conn,
192                                      SilcClientID *client_id,
193                                      SilcBuffer attributes,
194                                      SilcGetClientCallback completion,
195                                      void *context)
196 {
197   SilcClientGetClientInternal i;
198   SilcClientEntry client_entry;
199   SilcBuffer idp;
200   SilcUInt16 cmd_ident;
201
202   if (!client || !conn | !client_id)
203     return 0;
204
205   SILC_LOG_DEBUG(("Resolve client by ID (%s)",
206                   silc_id_render(client_id, SILC_ID_CLIENT)));
207
208   i = silc_calloc(1, sizeof(*i));
209   if (!i)
210     return 0;
211   i->completion = completion;
212   i->context = context;
213   i->clients = silc_dlist_init();
214   if (!i->clients) {
215     silc_free(i);
216     return 0;
217   }
218
219   /* Attach to resolving, if on going */
220   client_entry = silc_client_get_client_by_id(client, conn, client_id);
221   if (client_entry && client_entry->internal.resolve_cmd_ident) {
222     SILC_LOG_DEBUG(("Attach to existing resolving"));
223     silc_client_unref_client(client, conn, client_entry);
224     silc_client_command_pending(conn, SILC_COMMAND_NONE,
225                                 client_entry->internal.resolve_cmd_ident,
226                                 silc_client_get_clients_cb, i);
227     return client_entry->internal.resolve_cmd_ident;
228   }
229
230   /* Send the command */
231   idp = silc_id_payload_encode(client_id, SILC_ID_CLIENT);
232   cmd_ident = silc_client_command_send(client, conn, SILC_COMMAND_WHOIS,
233                                        silc_client_get_clients_cb, i,
234                                        2, 3, silc_buffer_datalen(attributes),
235                                        4, silc_buffer_datalen(idp));
236   if (!cmd_ident && completion)
237     completion(client, conn, SILC_STATUS_ERR_RESOURCE_LIMIT, NULL, context);
238
239   if (client_entry && cmd_ident)
240     client_entry->internal.resolve_cmd_ident = cmd_ident;
241
242   silc_client_unref_client(client, conn, client_entry);
243   silc_buffer_free(idp);
244
245   return cmd_ident;
246 }
247
248 /* Finds client entry or entries by the `nickname' and `server'. The
249    completion callback will be called when the client entries has been
250    found.  Used internally by the library. */
251
252 static SilcUInt16 silc_client_get_clients_i(SilcClient client,
253                                             SilcClientConnection conn,
254                                             SilcCommand command,
255                                             const char *nickname,
256                                             const char *server,
257                                             SilcBuffer attributes,
258                                             SilcGetClientCallback completion,
259                                             void *context)
260 {
261   SilcClientGetClientInternal i;
262   char userhost[768 + 1];
263   int len;
264
265   SILC_LOG_DEBUG(("Resolve client by %s command",
266                   silc_get_command_name(command)));
267
268   if (!client || !conn)
269     return 0;
270   if (!nickname && !attributes)
271     return 0;
272
273   i = silc_calloc(1, sizeof(*i));
274   if (!i)
275     return 0;
276   i->clients = silc_dlist_init();
277   if (!i->clients) {
278     silc_free(i);
279     return 0;
280   }
281   i->completion = completion;
282   i->context = context;
283
284   memset(userhost, 0, sizeof(userhost));
285   if (nickname && server) {
286     len = strlen(nickname) + strlen(server) + 3;
287     silc_strncat(userhost, len, nickname, strlen(nickname));
288     silc_strncat(userhost, len, "@", 1);
289     silc_strncat(userhost, len, server, strlen(server));
290   } else if (nickname) {
291     silc_strncat(userhost, sizeof(userhost) - 1, nickname, strlen(nickname));
292   }
293
294   /* Send the command */
295   if (command == SILC_COMMAND_IDENTIFY)
296     return silc_client_command_send(client, conn, SILC_COMMAND_IDENTIFY,
297                                     silc_client_get_clients_cb, i,
298                                     1, 1, userhost, strlen(userhost));
299   return silc_client_command_send(client, conn, SILC_COMMAND_WHOIS,
300                                   silc_client_get_clients_cb, i,
301                                   2, 1, userhost, strlen(userhost),
302                                   3, silc_buffer_datalen(attributes));
303 }
304
305 /* Get clients from server with IDENTIFY command */
306
307 SilcUInt16 silc_client_get_clients(SilcClient client,
308                                    SilcClientConnection conn,
309                                    const char *nickname,
310                                    const char *server,
311                                    SilcGetClientCallback completion,
312                                    void *context)
313 {
314   return silc_client_get_clients_i(client, conn, SILC_COMMAND_IDENTIFY,
315                                    nickname, server, NULL,
316                                    completion, context);
317 }
318
319 /* Get clients from server with WHOIS command */
320
321 SilcUInt16 silc_client_get_clients_whois(SilcClient client,
322                                          SilcClientConnection conn,
323                                          const char *nickname,
324                                          const char *server,
325                                          SilcBuffer attributes,
326                                          SilcGetClientCallback completion,
327                                          void *context)
328 {
329   return silc_client_get_clients_i(client, conn, SILC_COMMAND_WHOIS,
330                                    nickname, server, attributes,
331                                    completion, context);
332 }
333
334 /* ID list resolving context */
335 typedef struct {
336   SilcGetClientCallback completion;
337   void *context;
338   SilcBuffer client_id_list;
339   SilcUInt32 list_count;
340 } *GetClientsByListInternal;
341
342 static SilcBool silc_client_get_clients_list_cb(SilcClient client,
343                                                 SilcClientConnection conn,
344                                                 SilcCommand command,
345                                                 SilcStatus status,
346                                                 SilcStatus error,
347                                                 void *context,
348                                                 va_list ap)
349 {
350   GetClientsByListInternal i = context;
351   SilcClientEntry client_entry;
352   SilcDList clients;
353   SilcUInt16 idp_len;
354   SilcID id;
355   int c;
356
357   /* Process the list after all replies have been received */
358   if (status != SILC_STATUS_OK && !SILC_STATUS_IS_ERROR(status) &&
359       status != SILC_STATUS_LIST_END)
360     return TRUE;
361
362   SILC_LOG_DEBUG(("Resolved all clients"));
363
364   clients = silc_dlist_init();
365   if (!clients) {
366     status = SILC_STATUS_ERR_RESOURCE_LIMIT;
367     goto out;
368   }
369
370   for (c = 0; c < i->list_count; c++) {
371     /* Get Client ID */
372     SILC_GET16_MSB(idp_len, i->client_id_list->data + 2);
373     idp_len += 4;
374     if (!silc_id_payload_parse_id(i->client_id_list->data, idp_len, &id)) {
375       status = SILC_STATUS_ERR_BAD_CLIENT_ID;
376       goto out;
377     }
378
379     /* Get client entry */
380     client_entry = silc_client_get_client_by_id(client, conn, &id.u.client_id);
381     if (client_entry)
382       silc_dlist_add(clients, client_entry);
383
384     if (!silc_buffer_pull(i->client_id_list, idp_len)) {
385       status = SILC_STATUS_ERR_BAD_CLIENT_ID;
386       goto out;
387     }
388   }
389
390   silc_dlist_start(clients);
391   status = SILC_STATUS_OK;
392   if (i->completion)
393     i->completion(client, conn, status, clients, i->context);
394
395  out:
396   if (status != SILC_STATUS_OK && i->completion)
397     i->completion(client, conn, status, NULL, i->context);
398
399   silc_client_list_free(client, conn, clients);
400   silc_buffer_free(i->client_id_list);
401   silc_free(i);
402
403   return FALSE;
404 }
405
406 /* Gets client entries by the list of client ID's `client_id_list'. This
407    always resolves those client ID's it does not know yet from the server
408    so this function might take a while. The `client_id_list' is a list
409    of ID Payloads added one after other.  JOIN command reply and USERS
410    command reply for example returns this sort of list. The `completion'
411    will be called after the entries are available. */
412
413 SilcUInt16 silc_client_get_clients_by_list(SilcClient client,
414                                            SilcClientConnection conn,
415                                            SilcUInt32 list_count,
416                                            SilcBuffer client_id_list,
417                                            SilcGetClientCallback completion,
418                                            void *context)
419 {
420   GetClientsByListInternal in;
421   SilcClientEntry entry;
422   unsigned char **res_argv = NULL;
423   SilcUInt32 *res_argv_lens = NULL, *res_argv_types = NULL, res_argc = 0;
424   SilcUInt16 idp_len, cmd_ident;
425   SilcID id;
426   int i;
427
428   SILC_LOG_DEBUG(("Resolve clients from Client ID list"));
429
430   if (!client || !conn || !client_id_list)
431     return 0;
432
433   in = silc_calloc(1, sizeof(*in));
434   if (!in)
435     return 0;
436   in->completion = completion;
437   in->context = context;
438   in->list_count = list_count;
439   in->client_id_list = silc_buffer_copy(client_id_list);
440   if (!in->client_id_list)
441     goto err;
442
443   for (i = 0; i < list_count; i++) {
444     /* Get Client ID */
445     SILC_GET16_MSB(idp_len, client_id_list->data + 2);
446     idp_len += 4;
447     if (!silc_id_payload_parse_id(client_id_list->data, idp_len, &id))
448       goto err;
449
450     /* Check if we have this client cached already.  If we don't have the
451        entry or it has incomplete info, then resolve it from the server. */
452     entry = silc_client_get_client_by_id(client, conn, &id.u.client_id);
453     if (!entry || !entry->nickname[0] || !entry->username[0] ||
454         !entry->realname) {
455       if (!res_argv) {
456         res_argv = silc_calloc(list_count, sizeof(*res_argv));
457         res_argv_lens = silc_calloc(list_count, sizeof(*res_argv_lens));
458         res_argv_types = silc_calloc(list_count, sizeof(*res_argv_types));
459         if (!res_argv || !res_argv_lens || !res_argv_types) {
460           silc_client_unref_client(client, conn, entry);
461           goto err;
462         }
463       }
464
465       res_argv[res_argc] = client_id_list->data;
466       res_argv_lens[res_argc] = idp_len;
467       res_argv_types[res_argc] = res_argc + 4;
468       res_argc++;
469     }
470     silc_client_unref_client(client, conn, entry);
471
472     if (!silc_buffer_pull(client_id_list, idp_len))
473       goto err;
474   }
475   silc_buffer_start(client_id_list);
476
477   /* Query the unknown client information from server */
478   if (res_argc) {
479     cmd_ident = silc_client_command_send_argv(client,
480                                               conn, SILC_COMMAND_WHOIS,
481                                               silc_client_get_clients_list_cb,
482                                               in, res_argc, res_argv,
483                                               res_argv_lens,
484                                               res_argv_types);
485     silc_free(res_argv);
486     silc_free(res_argv_lens);
487     silc_free(res_argv_types);
488     return cmd_ident;
489   }
490
491   /* We have the clients in cache, get them and call the completion */
492   silc_client_get_clients_list_cb(client, conn, SILC_COMMAND_WHOIS,
493                                   SILC_STATUS_OK, SILC_STATUS_OK, in, NULL);
494   return 0;
495
496  err:
497   silc_buffer_free(in->client_id_list);
498   silc_free(in);
499   silc_free(res_argv);
500   silc_free(res_argv_lens);
501   silc_free(res_argv_types);
502   return 0;
503 }
504
505 #if 0
506 typedef struct {
507   SilcClient client;
508   SilcClientConnection conn;
509   SilcChannelID channel_id;
510   SilcGetClientCallback completion;
511   void *context;
512   int res_count;
513 } *GetClientsByChannelInternal;
514
515 SILC_CLIENT_CMD_FUNC(get_clients_by_channel_cb)
516 {
517   GetClientsByChannelInternal i = context;
518   SilcClientEntry *clients = NULL;
519   SilcUInt32 clients_count = 0;
520   SilcBool found = FALSE;
521   SilcChannelEntry channel;
522   SilcHashTableList htl;
523   SilcChannelUser chu;
524
525   if (i->res_count) {
526     i->res_count--;
527     if (i->res_count)
528       return;
529   }
530
531   channel = silc_client_get_channel_by_id(i->client, i->conn, &i->channel_id);
532   if (channel && !silc_hash_table_count(channel->user_list)) {
533     clients = silc_calloc(silc_hash_table_count(channel->user_list),
534                           sizeof(*clients));
535     silc_hash_table_list(channel->user_list, &htl);
536     while (silc_hash_table_get(&htl, NULL, (void *)&chu))
537       clients[clients_count++] = chu->client;
538     silc_hash_table_list_reset(&htl);
539     found = TRUE;
540   }
541
542   if (found) {
543     i->completion(i->client, i->conn, clients, clients_count, i->context);
544     silc_free(clients);
545   } else {
546     i->completion(i->client, i->conn, NULL, 0, i->context);
547   }
548
549   silc_free(i);
550 }
551
552 /* Gets client entries by the channel entry indicated by `channel'.  Thus,
553    it resolves the clients currently on that channel. */
554
555 void silc_client_get_clients_by_channel(SilcClient client,
556                                         SilcClientConnection conn,
557                                         SilcChannelEntry channel,
558                                         SilcGetClientCallback completion,
559                                         void *context)
560 {
561   GetClientsByChannelInternal in;
562   SilcHashTableList htl;
563   SilcChannelUser chu;
564   SilcClientEntry entry;
565   unsigned char **res_argv = NULL;
566   SilcUInt32 *res_argv_lens = NULL, *res_argv_types = NULL, res_argc = 0;
567   SilcBuffer idp;
568   SilcBool wait_res = FALSE;
569
570   assert(client && conn && channel);
571
572   SILC_LOG_DEBUG(("Start"));
573
574   in = silc_calloc(1, sizeof(*in));
575   in->client = client;
576   in->conn = conn;
577   in->channel_id = *channel->id;
578   in->completion = completion;
579   in->context = context;
580
581   /* If user list does not exist, send USERS command. */
582   if (!channel->user_list || !silc_hash_table_count(channel->user_list)) {
583     SILC_LOG_DEBUG(("Sending USERS"));
584     silc_client_command_register(client, SILC_COMMAND_USERS, NULL, NULL,
585                                  silc_client_command_reply_users_i, 0,
586                                  ++conn->cmd_ident);
587     silc_client_command_send(client, conn, SILC_COMMAND_USERS,
588                              conn->cmd_ident, 1, 2, channel->channel_name,
589                              strlen(channel->channel_name));
590     silc_client_command_pending(conn, SILC_COMMAND_USERS, conn->cmd_ident,
591                                 silc_client_command_get_clients_by_channel_cb,
592                                 in);
593     return;
594   }
595
596   silc_hash_table_list(channel->user_list, &htl);
597   while (silc_hash_table_get(&htl, NULL, (void *)&chu)) {
598     entry = chu->client;
599
600     /* If the entry has incomplete info, then resolve it from the server. */
601     if (!entry->nickname[0] || !entry->realname) {
602       if (entry->status & SILC_CLIENT_STATUS_RESOLVING) {
603         /* Attach to this resolving and wait until it finishes */
604         silc_client_command_pending(
605                             conn, SILC_COMMAND_NONE,
606                             entry->resolve_cmd_ident,
607                             silc_client_command_get_clients_by_channel_cb,
608                             (void *)in);
609         wait_res = TRUE;
610         in->res_count++;
611         continue;
612       }
613       entry->status |= SILC_CLIENT_STATUS_RESOLVING;
614       entry->resolve_cmd_ident = conn->cmd_ident + 1;
615
616       idp = silc_id_payload_encode(entry->id, SILC_ID_CLIENT);
617
618       /* No we don't have it, query it from the server. Assemble argument
619          table that will be sent for the WHOIS command later. */
620       res_argv = silc_realloc(res_argv, sizeof(*res_argv) *
621                               (res_argc + 1));
622       res_argv_lens = silc_realloc(res_argv_lens, sizeof(*res_argv_lens) *
623                                    (res_argc + 1));
624       res_argv_types = silc_realloc(res_argv_types, sizeof(*res_argv_types) *
625                                     (res_argc + 1));
626       res_argv[res_argc] = silc_memdup(idp->data, idp->len);
627       res_argv_lens[res_argc] = idp->len;
628       res_argv_types[res_argc] = res_argc + 4;
629       res_argc++;
630
631       silc_buffer_free(idp);
632     }
633   }
634   silc_hash_table_list_reset(&htl);
635
636   /* Query the client information from server if the list included clients
637      that we don't know about. */
638   if (res_argc) {
639     SilcBuffer res_cmd;
640
641     /* Send the WHOIS command to server */
642     res_cmd = silc_command_payload_encode(SILC_COMMAND_WHOIS,
643                                           res_argc, res_argv, res_argv_lens,
644                                           res_argv_types, ++conn->cmd_ident);
645     silc_client_packet_send(client, conn->sock, SILC_PACKET_COMMAND,
646                             NULL, 0, NULL, NULL, res_cmd->data, res_cmd->len,
647                             TRUE);
648
649     /* Register our own command reply for this command */
650     silc_client_command_register(client, SILC_COMMAND_WHOIS, NULL, NULL,
651                                  silc_client_command_reply_whois_i, 0,
652                                  conn->cmd_ident);
653
654     /* Process the applications request after reply has been received  */
655     silc_client_command_pending(
656                            conn, SILC_COMMAND_WHOIS, conn->cmd_ident,
657                            silc_client_command_get_clients_by_channel_cb,
658                            (void *)in);
659     in->res_count++;
660
661     silc_buffer_free(res_cmd);
662     silc_free(res_argv);
663     silc_free(res_argv_lens);
664     silc_free(res_argv_types);
665     return;
666   }
667
668   if (wait_res)
669     return;
670
671   /* We have the clients in cache, get them and call the completion */
672   silc_client_command_get_clients_by_channel_cb((void *)in, NULL);
673 }
674 #endif /* 0 */
675
676
677 /************************** Client Entry Routines ***************************/
678
679 /* Creates new client entry and adds it to the ID cache. Returns pointer
680    to the new entry. */
681
682 SilcClientEntry silc_client_add_client(SilcClient client,
683                                        SilcClientConnection conn,
684                                        char *nickname, char *username,
685                                        char *userinfo, SilcClientID *id,
686                                        SilcUInt32 mode)
687 {
688   SilcClientEntry client_entry;
689   char *nick = NULL;
690
691   SILC_LOG_DEBUG(("Adding new client entry"));
692
693   /* Save the client infos */
694   client_entry = silc_calloc(1, sizeof(*client_entry));
695   if (!client_entry)
696     return NULL;
697
698   silc_atomic_init8(&client_entry->internal.refcnt, 0);
699   client_entry->id = *id;
700   client_entry->internal.valid = TRUE;
701   client_entry->mode = mode;
702   client_entry->realname = userinfo ? strdup(userinfo) : NULL;
703   silc_parse_userfqdn(nickname, client_entry->nickname,
704                       sizeof(client_entry->nickname),
705                       client_entry->server,
706                       sizeof(client_entry->server));
707   silc_parse_userfqdn(username, client_entry->username,
708                       sizeof(client_entry->username),
709                       client_entry->hostname,
710                       sizeof(client_entry->hostname));
711   client_entry->channels = silc_hash_table_alloc(1, silc_hash_ptr, NULL, NULL,
712                                                  NULL, NULL, NULL, TRUE);
713   if (!client_entry->channels) {
714     silc_free(client_entry->realname);
715     silc_free(client_entry);
716     return NULL;
717   }
718
719   /* Normalize nickname */
720   if (client_entry->nickname[0]) {
721     nick = silc_identifier_check(client_entry->nickname,
722                                  strlen(client_entry->nickname),
723                                  SILC_STRING_UTF8, 128, NULL);
724     if (!nick) {
725       silc_free(client_entry->realname);
726       silc_hash_table_free(client_entry->channels);
727       silc_free(client_entry);
728       return NULL;
729     }
730   }
731
732   /* Format the nickname */
733   silc_client_nickname_format(client, conn, client_entry);
734
735   silc_mutex_lock(conn->internal->lock);
736
737   /* Add client to cache, the normalized nickname is saved to cache */
738   if (!silc_idcache_add(conn->internal->client_cache, nick,
739                         &client_entry->id, client_entry)) {
740     silc_free(nick);
741     silc_free(client_entry->realname);
742     silc_hash_table_free(client_entry->channels);
743     silc_free(client_entry);
744     silc_mutex_unlock(conn->internal->lock);
745     return NULL;
746   }
747
748   client_entry->nickname_normalized = nick;
749
750   silc_mutex_unlock(conn->internal->lock);
751   silc_client_ref_client(client, conn, client_entry);
752
753   SILC_LOG_DEBUG(("Added %p", client_entry));
754
755   return client_entry;
756 }
757
758 /* Updates the `client_entry' with the new information sent as argument. */
759
760 void silc_client_update_client(SilcClient client,
761                                SilcClientConnection conn,
762                                SilcClientEntry client_entry,
763                                const char *nickname,
764                                const char *username,
765                                const char *userinfo,
766                                SilcUInt32 mode)
767 {
768   char *nick = NULL;
769
770   SILC_LOG_DEBUG(("Update client entry"));
771
772   if (!client_entry->realname && userinfo)
773     client_entry->realname = strdup(userinfo);
774   if ((!client_entry->username[0] || !client_entry->hostname[0]) && username)
775     silc_parse_userfqdn(username, client_entry->username,
776                         sizeof(client_entry->username),
777                         client_entry->hostname,
778                         sizeof(client_entry->username));
779   if (!client_entry->nickname[0] && nickname) {
780     silc_parse_userfqdn(nickname, client_entry->nickname,
781                         sizeof(client_entry->nickname),
782                         client_entry->server,
783                         sizeof(client_entry->server));
784
785     /* Normalize nickname */
786     nick = silc_identifier_check(client_entry->nickname,
787                                  strlen(client_entry->nickname),
788                                  SILC_STRING_UTF8, 128, NULL);
789     if (!nick)
790       return;
791
792     /* Format nickname */
793     silc_client_nickname_format(client, conn, client_entry);
794
795     /* Update cache entry */
796     silc_mutex_lock(conn->internal->lock);
797     silc_idcache_update_by_context(conn->internal->client_cache,
798                                    client_entry, NULL, nick, TRUE);
799     silc_mutex_unlock(conn->internal->lock);
800     client_entry->nickname_normalized = nick;
801   }
802   client_entry->mode = mode;
803 }
804
805 /* Deletes the client entry and frees all memory. */
806
807 void silc_client_del_client_entry(SilcClient client,
808                                   SilcClientConnection conn,
809                                   SilcClientEntry client_entry)
810 {
811   silc_free(client_entry->realname);
812   silc_free(client_entry->nickname_normalized);
813   silc_free(client_entry->internal.key);
814   if (client_entry->public_key)
815     silc_pkcs_public_key_free(client_entry->public_key);
816   silc_hash_table_free(client_entry->channels);
817   if (client_entry->internal.send_key)
818     silc_cipher_free(client_entry->internal.send_key);
819   if (client_entry->internal.receive_key)
820     silc_cipher_free(client_entry->internal.receive_key);
821   if (client_entry->internal.hmac_send)
822     silc_hmac_free(client_entry->internal.hmac_send);
823   if (client_entry->internal.hmac_receive)
824     silc_hmac_free(client_entry->internal.hmac_receive);
825 #if 0
826   silc_client_ftp_session_free_client(conn, client_entry);
827   if (client_entry->internal->ke)
828     silc_client_abort_key_agreement(client, conn, client_entry);
829 #endif /* 0 */
830   silc_atomic_uninit8(&client_entry->internal.refcnt);
831   silc_free(client_entry);
832 }
833
834 /* Removes client from the cache by the client entry. */
835
836 SilcBool silc_client_del_client(SilcClient client, SilcClientConnection conn,
837                                 SilcClientEntry client_entry)
838 {
839   SilcBool ret;
840
841   if (!client_entry)
842     return FALSE;
843
844   if (silc_atomic_sub_int8(&client_entry->internal.refcnt, 1) > 0)
845     return FALSE;
846
847   SILC_LOG_DEBUG(("Deleting client %p", client_entry));
848
849   silc_mutex_lock(conn->internal->lock);
850   ret = silc_idcache_del_by_context(conn->internal->client_cache,
851                                     client_entry, NULL);
852   silc_mutex_unlock(conn->internal->lock);
853
854   if (ret) {
855     /* Remove from channels */
856     silc_client_remove_from_channels(client, conn, client_entry);
857
858     /* Free the client entry data */
859     silc_client_del_client_entry(client, conn, client_entry);
860   }
861
862   return ret;
863 }
864
865 /* Take reference of client entry */
866
867 void silc_client_ref_client(SilcClient client, SilcClientConnection conn,
868                             SilcClientEntry client_entry)
869 {
870   silc_atomic_add_int8(&client_entry->internal.refcnt, 1);
871   SILC_LOG_DEBUG(("Client %p refcnt %d->%d", client_entry,
872                   silc_atomic_get_int8(&client_entry->internal.refcnt) - 1,
873                   silc_atomic_get_int8(&client_entry->internal.refcnt)));
874 }
875
876 /* Release reference of client entry */
877
878 void silc_client_unref_client(SilcClient client, SilcClientConnection conn,
879                               SilcClientEntry client_entry)
880 {
881   if (client_entry) {
882     SILC_LOG_DEBUG(("Client %p refcnt %d->%d", client_entry,
883                     silc_atomic_get_int8(&client_entry->internal.refcnt),
884                     silc_atomic_get_int8(&client_entry->internal.refcnt) - 1));
885     silc_client_del_client(client, conn, client_entry);
886   }
887 }
888
889 /* Free client entry list */
890
891 void silc_client_list_free(SilcClient client, SilcClientConnection conn,
892                            SilcDList client_list)
893 {
894   SilcClientEntry client_entry;
895
896   if (client_list) {
897     silc_dlist_start(client_list);
898     while ((client_entry = silc_dlist_get(client_list)))
899       silc_client_unref_client(client, conn, client_entry);
900
901     silc_dlist_uninit(client_list);
902   }
903 }
904
905 /* Formats the nickname of the client specified by the `client_entry'.
906    If the format is specified by the application this will format the
907    nickname and replace the old nickname in the client entry. If the
908    format string is not specified then this function has no effect. */
909
910 void silc_client_nickname_format(SilcClient client,
911                                  SilcClientConnection conn,
912                                  SilcClientEntry client_entry)
913 {
914   char *cp;
915   char newnick[128 + 1];
916   int i, off = 0, len;
917   SilcBool freebase;
918   SilcDList clients;
919   SilcClientEntry entry, unformatted = NULL;
920
921   SILC_LOG_DEBUG(("Start"));
922
923   if (!client->internal->params->nickname_format[0])
924     return;
925
926   if (!client_entry->nickname[0])
927     return;
928
929   /* Get all clients with same nickname. Do not perform the formatting
930      if there aren't any clients with same nickname unless the application
931      is forcing us to do so. */
932   clients = silc_client_get_clients_local(client, conn,
933                                           client_entry->nickname, NULL);
934   if (!clients && !client->internal->params->nickname_force_format)
935     return;
936
937   if (clients) {
938     len = 0;
939     freebase = TRUE;
940     while ((entry = silc_dlist_get(clients))) {
941       if (entry->internal.valid && entry != client_entry)
942         len++;
943       if (entry->internal.valid && entry != client_entry &&
944           silc_utf8_strcasecmp(entry->nickname, client_entry->nickname)) {
945         freebase = FALSE;
946         unformatted = entry;
947       }
948     }
949     if (!len || freebase) {
950       silc_client_list_free(client, conn, clients);
951       return;
952     }
953   }
954
955   /* If we are changing nickname of our local entry we'll enforce
956      that we will always get the unformatted nickname.  Give our
957      format number to the one that is not formatted now. */
958   if (unformatted && client_entry == conn->local_entry)
959     client_entry = unformatted;
960
961   memset(newnick, 0, sizeof(newnick));
962   cp = client->internal->params->nickname_format;
963   while (cp && *cp) {
964     if (*cp == '%') {
965       cp++;
966       continue;
967     }
968
969     switch(*cp) {
970     case 'n':
971       /* Nickname */
972       if (!client_entry->nickname[0])
973         break;
974       len = strlen(client_entry->nickname);
975       memcpy(&newnick[off], client_entry->nickname, len);
976       off += len;
977       break;
978     case 'h':
979       /* Stripped hostname */
980       if (!client_entry->hostname[0])
981         break;
982       len = strcspn(client_entry->hostname, ".");
983       i = strcspn(client_entry->hostname, "-");
984       if (i < len)
985         len = i;
986       memcpy(&newnick[off], client_entry->hostname, len);
987       off += len;
988       break;
989     case 'H':
990       /* Full hostname */
991       if (!client_entry->hostname[0])
992         break;
993       len = strlen(client_entry->hostname);
994       memcpy(&newnick[off], client_entry->hostname, len);
995       off += len;
996       break;
997     case 's':
998       /* Stripped server name */
999       if (!client_entry->server)
1000         break;
1001       len = strcspn(client_entry->server, ".");
1002       memcpy(&newnick[off], client_entry->server, len);
1003       off += len;
1004       break;
1005     case 'S':
1006       /* Full server name */
1007       if (!client_entry->server)
1008         break;
1009       len = strlen(client_entry->server);
1010       memcpy(&newnick[off], client_entry->server, len);
1011       off += len;
1012       break;
1013     case 'a':
1014       /* Ascending number */
1015       {
1016         char tmp[6];
1017         int num, max = 1;
1018
1019         if (clients && silc_dlist_count(clients) == 1)
1020           break;
1021
1022         if (clients) {
1023           silc_dlist_start(clients);
1024           while ((entry = silc_dlist_get(clients))) {
1025             if (!silc_utf8_strncasecmp(entry->nickname, newnick, off))
1026               continue;
1027             if (strlen(entry->nickname) <= off)
1028               continue;
1029             num = atoi(&entry->nickname[off]);
1030             if (num > max)
1031               max = num;
1032           }
1033         }
1034
1035         memset(tmp, 0, sizeof(tmp));
1036         snprintf(tmp, sizeof(tmp) - 1, "%d", ++max);
1037         len = strlen(tmp);
1038         memcpy(&newnick[off], tmp, len);
1039         off += len;
1040       }
1041       break;
1042     default:
1043       /* Some other character in the string */
1044       memcpy(&newnick[off], cp, 1);
1045       off++;
1046       break;
1047     }
1048
1049     cp++;
1050   }
1051
1052   newnick[off] = 0;
1053   memcpy(client_entry->nickname, newnick, strlen(newnick));
1054   silc_client_list_free(client, conn, clients);
1055 }
1056
1057 /************************ Channel Searching Locally *************************/
1058
1059 /* Finds entry for channel by the channel name. Returns the entry or NULL
1060    if the entry was not found. It is found only if the client is joined
1061    to the channel. */
1062
1063 SilcChannelEntry silc_client_get_channel(SilcClient client,
1064                                          SilcClientConnection conn,
1065                                          char *channel)
1066 {
1067   SilcIDCacheEntry id_cache;
1068   SilcChannelEntry entry;
1069
1070   if (!client || !conn || !channel)
1071     return NULL;
1072
1073   SILC_LOG_DEBUG(("Find channel %s", channel));
1074
1075   /* Normalize name for search */
1076   channel = silc_channel_name_check(channel, strlen(channel), SILC_STRING_UTF8,
1077                                     256, NULL);
1078   if (!channel)
1079     return NULL;
1080
1081   silc_mutex_lock(conn->internal->lock);
1082
1083   if (!silc_idcache_find_by_name_one(conn->internal->channel_cache, channel,
1084                                      &id_cache)) {
1085     silc_mutex_unlock(conn->internal->lock);
1086     silc_free(channel);
1087     return NULL;
1088   }
1089
1090   SILC_LOG_DEBUG(("Found"));
1091
1092   entry = id_cache->context;
1093
1094   /* Reference */
1095   silc_client_ref_channel(client, conn, entry);
1096   silc_mutex_unlock(conn->internal->lock);
1097
1098   silc_free(channel);
1099
1100   return entry;
1101 }
1102
1103 /* Finds entry for channel by the channel ID. Returns the entry or NULL
1104    if the entry was not found. It is found only if the client is joined
1105    to the channel. */
1106
1107 SilcChannelEntry silc_client_get_channel_by_id(SilcClient client,
1108                                                SilcClientConnection conn,
1109                                                SilcChannelID *channel_id)
1110 {
1111   SilcIDCacheEntry id_cache;
1112   SilcChannelEntry entry;
1113
1114   if (!client || !conn || !channel_id)
1115     return NULL;
1116
1117   SILC_LOG_DEBUG(("Find channel by id %s",
1118                   silc_id_render(channel_id, SILC_ID_CHANNEL)));
1119
1120   silc_mutex_lock(conn->internal->lock);
1121
1122   if (!silc_idcache_find_by_id_one(conn->internal->channel_cache, channel_id,
1123                                    &id_cache)) {
1124     silc_mutex_unlock(conn->internal->lock);
1125     return NULL;
1126   }
1127
1128   SILC_LOG_DEBUG(("Found"));
1129
1130   entry = id_cache->context;
1131
1132   /* Reference */
1133   silc_client_ref_channel(client, conn, entry);
1134   silc_mutex_unlock(conn->internal->lock);
1135
1136   return entry;
1137 }
1138
1139 /********************** Channel Resolving from Server ***********************/
1140
1141 /* Channel resolving context */
1142 typedef struct {
1143   SilcDList channels;
1144   SilcGetChannelCallback completion;
1145   void *context;
1146 } *SilcClientGetChannelInternal;
1147
1148 /* Resolving command callback */
1149
1150 static SilcBool silc_client_get_channel_cb(SilcClient client,
1151                                            SilcClientConnection conn,
1152                                            SilcCommand command,
1153                                            SilcStatus status,
1154                                            SilcStatus error,
1155                                            void *context,
1156                                            va_list ap)
1157 {
1158   SilcClientGetChannelInternal i = context;
1159   SilcChannelEntry entry;
1160
1161   if (error != SILC_STATUS_OK) {
1162     SILC_LOG_DEBUG(("Resolving failed: %s", silc_get_status_message(error)));
1163     if (i->completion)
1164       i->completion(client, conn, error, NULL, i->context);
1165     goto out;
1166   }
1167
1168   /* Add the returned channel to list */
1169   if (i->completion) {
1170     entry = va_arg(ap, SilcChannelEntry);
1171     silc_client_ref_channel(client, conn, entry);
1172     silc_dlist_add(i->channels, entry);
1173   }
1174
1175   if (status == SILC_STATUS_OK || status == SILC_STATUS_LIST_END) {
1176     /* Deliver the channels to the caller */
1177     if (i->completion) {
1178       SILC_LOG_DEBUG(("Resolved %d channels", silc_dlist_count(i->channels)));
1179       silc_dlist_start(i->channels);
1180       i->completion(client, conn, SILC_STATUS_OK, i->channels, i->context);
1181     }
1182     goto out;
1183   }
1184
1185   return TRUE;
1186
1187  out:
1188   silc_client_list_free_channels(client, conn, i->channels);
1189   silc_free(i);
1190   return FALSE;
1191 }
1192
1193 /* Resolves channel entry from the server by the channel name. */
1194
1195 void silc_client_get_channel_resolve(SilcClient client,
1196                                      SilcClientConnection conn,
1197                                      char *channel_name,
1198                                      SilcGetChannelCallback completion,
1199                                      void *context)
1200 {
1201   SilcClientGetChannelInternal i;
1202
1203   if (!client || !conn || !channel_name || !completion)
1204     return;
1205
1206   SILC_LOG_DEBUG(("Resolve channel %s", channel_name));
1207
1208   i = silc_calloc(1, sizeof(*i));
1209   if (!i)
1210     return;
1211   i->completion = completion;
1212   i->context = context;
1213   i->channels = silc_dlist_init();
1214   if (!i->channels) {
1215     silc_free(i);
1216     return;
1217   }
1218
1219   /* Send the command */
1220   if (!silc_client_command_send(client, conn, SILC_COMMAND_IDENTIFY,
1221                                 silc_client_get_channel_cb, i, 1,
1222                                 3, channel_name, strlen(channel_name))) {
1223     if (completion)
1224       completion(client, conn, SILC_STATUS_ERR_RESOURCE_LIMIT, NULL, context);
1225   }
1226 }
1227
1228 /* Resolves channel information from the server by the channel ID. */
1229
1230 SilcUInt16
1231 silc_client_get_channel_by_id_resolve(SilcClient client,
1232                                       SilcClientConnection conn,
1233                                       SilcChannelID *channel_id,
1234                                       SilcGetChannelCallback completion,
1235                                       void *context)
1236 {
1237   SilcClientGetChannelInternal i;
1238   SilcBuffer idp;
1239   SilcUInt16 cmd_ident;
1240
1241   if (!client || !conn || !channel_id || !completion)
1242     return 0;
1243
1244   SILC_LOG_DEBUG(("Resolve channel by id %s",
1245                   silc_id_render(channel_id, SILC_ID_CHANNEL)));
1246
1247   i = silc_calloc(1, sizeof(*i));
1248   if (!i)
1249     return 0;
1250   i->completion = completion;
1251   i->context = context;
1252   i->channels = silc_dlist_init();
1253   if (!i->channels) {
1254     silc_free(i);
1255     return 0;
1256   }
1257
1258   /* Send the command */
1259   idp = silc_id_payload_encode(channel_id, SILC_ID_CHANNEL);
1260   cmd_ident = silc_client_command_send(client, conn, SILC_COMMAND_IDENTIFY,
1261                                        silc_client_get_channel_cb, i, 1,
1262                                        5, silc_buffer_datalen(idp));
1263   silc_buffer_free(idp);
1264   if (!cmd_ident && completion)
1265     completion(client, conn, SILC_STATUS_ERR_RESOURCE_LIMIT, NULL, context);
1266
1267   return cmd_ident;
1268 }
1269
1270 /************************* Channel Entry Routines ***************************/
1271
1272 /* Add new channel entry to the ID Cache */
1273
1274 SilcChannelEntry silc_client_add_channel(SilcClient client,
1275                                          SilcClientConnection conn,
1276                                          const char *channel_name,
1277                                          SilcUInt32 mode,
1278                                          SilcChannelID *channel_id)
1279 {
1280   SilcChannelEntry channel;
1281   char *channel_namec;
1282
1283   SILC_LOG_DEBUG(("Start"));
1284
1285   channel = silc_calloc(1, sizeof(*channel));
1286   if (!channel)
1287     return NULL;
1288
1289   silc_atomic_init8(&channel->internal.refcnt, 0);
1290   channel->id = *channel_id;
1291   channel->mode = mode;
1292
1293   channel->channel_name = strdup(channel_name);
1294   if (!channel->channel_name) {
1295     silc_free(channel);
1296     return NULL;
1297   }
1298
1299   channel->user_list = silc_hash_table_alloc(1, silc_hash_ptr, NULL, NULL,
1300                                              NULL, NULL, NULL, TRUE);
1301   if (!channel->user_list) {
1302     silc_free(channel->channel_name);
1303     silc_free(channel);
1304     return NULL;
1305   }
1306
1307   /* Normalize channel name */
1308   channel_namec = silc_channel_name_check(channel_name, strlen(channel_name),
1309                                           SILC_STRING_UTF8, 256, NULL);
1310   if (!channel_namec) {
1311     silc_free(channel->channel_name);
1312     silc_hash_table_free(channel->user_list);
1313     silc_free(channel);
1314     return NULL;
1315   }
1316
1317   silc_mutex_lock(conn->internal->lock);
1318
1319   /* Add channel to cache, the normalized channel name is saved to cache */
1320   if (!silc_idcache_add(conn->internal->channel_cache, channel_namec,
1321                         &channel->id, channel)) {
1322     silc_free(channel_namec);
1323     silc_free(channel->channel_name);
1324     silc_hash_table_free(channel->user_list);
1325     silc_free(channel);
1326     silc_mutex_unlock(conn->internal->lock);
1327     return NULL;
1328   }
1329
1330   silc_mutex_unlock(conn->internal->lock);
1331   silc_client_ref_channel(client, conn, channel);
1332
1333   SILC_LOG_DEBUG(("Added %p", channel));
1334
1335   return channel;
1336 }
1337
1338 /* Removes channel from the cache by the channel entry. */
1339
1340 SilcBool silc_client_del_channel(SilcClient client, SilcClientConnection conn,
1341                                  SilcChannelEntry channel)
1342 {
1343   SilcBool ret;
1344   SilcCipher key;
1345   SilcHmac hmac;
1346
1347   if (!channel)
1348     return FALSE;
1349
1350   if (silc_atomic_sub_int8(&channel->internal.refcnt, 1) > 0)
1351     return FALSE;
1352
1353   SILC_LOG_DEBUG(("Deleting channel %p", channel));
1354
1355   silc_mutex_lock(conn->internal->lock);
1356   ret = silc_idcache_del_by_context(conn->internal->channel_cache,
1357                                     channel, NULL);
1358   silc_mutex_unlock(conn->internal->lock);
1359
1360   if (!ret)
1361     return FALSE;
1362
1363   silc_client_empty_channel(client, conn, channel);
1364   silc_hash_table_free(channel->user_list);
1365   silc_free(channel->channel_name);
1366   silc_free(channel->topic);
1367   if (channel->founder_key)
1368     silc_pkcs_public_key_free(channel->founder_key);
1369   if (channel->internal.channel_key)
1370     silc_cipher_free(channel->internal.channel_key);
1371   if (channel->internal.hmac)
1372     silc_hmac_free(channel->internal.hmac);
1373   if (channel->internal.old_channel_keys) {
1374     silc_dlist_start(channel->internal.old_channel_keys);
1375     while ((key = silc_dlist_get(channel->internal.old_channel_keys)))
1376       silc_cipher_free(key);
1377     silc_dlist_uninit(channel->internal.old_channel_keys);
1378   }
1379   if (channel->internal.old_hmacs) {
1380     silc_dlist_start(channel->internal.old_hmacs);
1381     while ((hmac = silc_dlist_get(channel->internal.old_hmacs)))
1382       silc_hmac_free(hmac);
1383     silc_dlist_uninit(channel->internal.old_hmacs);
1384   }
1385   silc_client_del_channel_private_keys(client, conn, channel);
1386   silc_atomic_uninit8(&channel->internal.refcnt);
1387   silc_schedule_task_del_by_context(conn->client->schedule, channel);
1388   silc_free(channel);
1389
1390   return ret;
1391 }
1392
1393 /* Replaces the channel ID of the `channel' to `new_id'. Returns FALSE
1394    if the ID could not be changed. */
1395
1396 SilcBool silc_client_replace_channel_id(SilcClient client,
1397                                         SilcClientConnection conn,
1398                                         SilcChannelEntry channel,
1399                                         SilcChannelID *new_id)
1400 {
1401   SilcBool ret = FALSE;
1402
1403   if (!new_id)
1404     return FALSE;
1405
1406   SILC_LOG_DEBUG(("Old Channel ID id(%s)",
1407                   silc_id_render(&channel->id, SILC_ID_CHANNEL)));
1408   SILC_LOG_DEBUG(("New Channel ID id(%s)",
1409                   silc_id_render(new_id, SILC_ID_CHANNEL)));
1410
1411   /* Update the ID */
1412   silc_mutex_lock(conn->internal->lock);
1413   silc_idcache_update_by_context(conn->internal->channel_cache, channel,
1414                                  new_id, NULL, FALSE);
1415   silc_mutex_unlock(conn->internal->lock);
1416
1417   return ret;
1418 }
1419
1420 /* Take reference of channel entry */
1421
1422 void silc_client_ref_channel(SilcClient client, SilcClientConnection conn,
1423                              SilcChannelEntry channel_entry)
1424 {
1425   silc_atomic_add_int8(&channel_entry->internal.refcnt, 1);
1426   SILC_LOG_DEBUG(("Channel %p refcnt %d->%d", channel_entry,
1427                   silc_atomic_get_int8(&channel_entry->internal.refcnt) - 1,
1428                   silc_atomic_get_int8(&channel_entry->internal.refcnt)));
1429 }
1430
1431 /* Release reference of channel entry */
1432
1433 void silc_client_unref_channel(SilcClient client, SilcClientConnection conn,
1434                                SilcChannelEntry channel_entry)
1435 {
1436   if (channel_entry) {
1437     SILC_LOG_DEBUG(("Channel %p refcnt %d->%d", channel_entry,
1438                     silc_atomic_get_int8(&channel_entry->internal.refcnt),
1439                     silc_atomic_get_int8(&channel_entry->internal.refcnt)
1440                     - 1));
1441     silc_client_del_channel(client, conn, channel_entry);
1442   }
1443 }
1444
1445 /* Free channel entry list */
1446
1447 void silc_client_list_free_channels(SilcClient client,
1448                                     SilcClientConnection conn,
1449                                     SilcDList channel_list)
1450 {
1451   SilcChannelEntry channel_entry;
1452
1453   if (channel_list) {
1454     silc_dlist_start(channel_list);
1455     while ((channel_entry = silc_dlist_get(channel_list)))
1456       silc_client_unref_channel(client, conn, channel_entry);
1457
1458     silc_dlist_uninit(channel_list);
1459   }
1460 }
1461
1462 /************************* Server Searching Locally *************************/
1463
1464 /* Finds entry for server by the server name. */
1465
1466 SilcServerEntry silc_client_get_server(SilcClient client,
1467                                        SilcClientConnection conn,
1468                                        char *server_name)
1469 {
1470   SilcIDCacheEntry id_cache;
1471   SilcServerEntry entry;
1472
1473   if (!client || !conn || !server_name)
1474     return NULL;
1475
1476   SILC_LOG_DEBUG(("Find server by name %s", server_name));
1477
1478   /* Normalize server name for search */
1479   server_name = silc_identifier_check(server_name, strlen(server_name),
1480                                       SILC_STRING_UTF8, 256, NULL);
1481   if (!server_name)
1482     return NULL;
1483
1484   silc_mutex_lock(conn->internal->lock);
1485
1486   if (!silc_idcache_find_by_name_one(conn->internal->server_cache,
1487                                      server_name, &id_cache)) {
1488     silc_free(server_name);
1489     silc_mutex_unlock(conn->internal->lock);
1490     return NULL;
1491   }
1492
1493   SILC_LOG_DEBUG(("Found"));
1494
1495   /* Reference */
1496   entry = id_cache->context;
1497   silc_client_ref_server(client, conn, entry);
1498
1499   silc_mutex_unlock(conn->internal->lock);
1500
1501   silc_free(server_name);
1502
1503   return entry;
1504 }
1505
1506 /* Finds entry for server by the server ID. */
1507
1508 SilcServerEntry silc_client_get_server_by_id(SilcClient client,
1509                                              SilcClientConnection conn,
1510                                              SilcServerID *server_id)
1511 {
1512   SilcIDCacheEntry id_cache;
1513   SilcServerEntry entry;
1514
1515   if (!client || !conn || !server_id)
1516     return NULL;
1517
1518   SILC_LOG_DEBUG(("Find server by id %s",
1519                   silc_id_render(server_id, SILC_ID_SERVER)));
1520
1521   silc_mutex_lock(conn->internal->lock);
1522
1523   if (!silc_idcache_find_by_id_one(conn->internal->server_cache,
1524                                    server_id, &id_cache)) {
1525     silc_mutex_unlock(conn->internal->lock);
1526     return NULL;
1527   }
1528
1529   SILC_LOG_DEBUG(("Found"));
1530
1531   /* Reference */
1532   entry = id_cache->context;
1533   silc_client_ref_server(client, conn, entry);
1534
1535   silc_mutex_unlock(conn->internal->lock);
1536
1537   return entry;
1538 }
1539
1540 /*********************** Server Resolving from Server ***********************/
1541
1542 /* Resolving context */
1543 typedef struct {
1544   SilcDList servers;
1545   SilcGetServerCallback completion;
1546   void *context;
1547 } *SilcClientGetServerInternal;
1548
1549 /* Resolving command callback */
1550
1551 static SilcBool silc_client_get_server_cb(SilcClient client,
1552                                           SilcClientConnection conn,
1553                                           SilcCommand command,
1554                                           SilcStatus status,
1555                                           SilcStatus error,
1556                                           void *context,
1557                                           va_list ap)
1558 {
1559   SilcClientGetServerInternal i = context;
1560   SilcServerEntry server;
1561
1562   if (error != SILC_STATUS_OK) {
1563     SILC_LOG_DEBUG(("Resolving failed: %s", silc_get_status_message(error)));
1564     if (i->completion)
1565       i->completion(client, conn, error, NULL, i->context);
1566     goto out;
1567   }
1568
1569   /* Add the returned servers to list */
1570   if (i->completion) {
1571     server = va_arg(ap, SilcServerEntry);
1572     silc_client_ref_server(client, conn, server);
1573     silc_dlist_add(i->servers, server);
1574     server->internal.resolve_cmd_ident = 0;
1575   }
1576
1577   if (status == SILC_STATUS_OK || status == SILC_STATUS_LIST_END) {
1578     /* Deliver the servers to the caller */
1579     if (i->completion) {
1580       SILC_LOG_DEBUG(("Resolved %d servers", silc_dlist_count(i->servers)));
1581       silc_dlist_start(i->servers);
1582       i->completion(client, conn, SILC_STATUS_OK, i->servers, i->context);
1583     }
1584     goto out;
1585   }
1586
1587   return TRUE;
1588
1589  out:
1590   silc_client_list_free_servers(client, conn, i->servers);
1591   silc_free(i);
1592   return FALSE;
1593 }
1594
1595 /* Resolve server by server ID */
1596
1597 SilcUInt16
1598 silc_client_get_server_by_id_resolve(SilcClient client,
1599                                      SilcClientConnection conn,
1600                                      SilcServerID *server_id,
1601                                      SilcGetServerCallback completion,
1602                                      void *context)
1603 {
1604   SilcClientGetServerInternal i;
1605   SilcServerEntry server;
1606   SilcBuffer idp;
1607   SilcUInt16 cmd_ident;
1608
1609   if (!client || !conn || !server_id || !completion)
1610     return 0;
1611
1612   SILC_LOG_DEBUG(("Resolve server by id %s",
1613                   silc_id_render(server_id, SILC_ID_SERVER)));
1614
1615   i = silc_calloc(1, sizeof(*i));
1616   if (!i)
1617     return 0;
1618   i->completion = completion;
1619   i->context = context;
1620   i->servers = silc_dlist_init();
1621   if (!i->servers) {
1622     silc_free(i);
1623     return 0;
1624   }
1625
1626   /* Attach to resolving, if on going */
1627   server = silc_client_get_server_by_id(client, conn, server_id);
1628   if (server && server->internal.resolve_cmd_ident) {
1629     SILC_LOG_DEBUG(("Attach to existing resolving"));
1630     silc_client_unref_server(client, conn, server);
1631     silc_client_command_pending(conn, SILC_COMMAND_NONE,
1632                                 server->internal.resolve_cmd_ident,
1633                                 silc_client_get_server_cb, i);
1634     return server->internal.resolve_cmd_ident;
1635   }
1636
1637   /* Send the command */
1638   idp = silc_id_payload_encode(server_id, SILC_ID_SERVER);
1639   cmd_ident = silc_client_command_send(client, conn, SILC_COMMAND_IDENTIFY,
1640                                        silc_client_get_server_cb, i, 1,
1641                                        5, silc_buffer_datalen(idp));
1642   silc_buffer_free(idp);
1643   if (!cmd_ident && completion)
1644     completion(client, conn, SILC_STATUS_ERR_RESOURCE_LIMIT, NULL, context);
1645
1646   if (server && cmd_ident)
1647     server->internal.resolve_cmd_ident = cmd_ident;
1648
1649   silc_client_unref_server(client, conn, server);
1650
1651   return cmd_ident;
1652 }
1653
1654 /************************** Server Entry Routines ***************************/
1655
1656 /* Add new server entry */
1657
1658 SilcServerEntry silc_client_add_server(SilcClient client,
1659                                        SilcClientConnection conn,
1660                                        const char *server_name,
1661                                        const char *server_info,
1662                                        SilcServerID *server_id)
1663 {
1664   SilcServerEntry server_entry;
1665   char *server_namec = NULL;
1666
1667   if (!server_id)
1668     return NULL;
1669
1670   SILC_LOG_DEBUG(("Adding new server %s", server_name));
1671
1672   server_entry = silc_calloc(1, sizeof(*server_entry));
1673   if (!server_entry)
1674     return NULL;
1675
1676   silc_atomic_init8(&server_entry->internal.refcnt, 0);
1677   server_entry->id = *server_id;
1678   if (server_name)
1679     server_entry->server_name = strdup(server_name);
1680   if (server_info)
1681     server_entry->server_info = strdup(server_info);
1682
1683   /* Normalize server name */
1684   if (server_name) {
1685     server_namec = silc_identifier_check(server_name, strlen(server_name),
1686                                          SILC_STRING_UTF8, 256, NULL);
1687     if (!server_namec) {
1688       silc_free(server_entry->server_name);
1689       silc_free(server_entry->server_info);
1690       silc_free(server_entry);
1691       return NULL;
1692     }
1693   }
1694
1695   silc_mutex_lock(conn->internal->lock);
1696
1697   /* Add server to cache */
1698   if (!silc_idcache_add(conn->internal->server_cache, server_namec,
1699                         &server_entry->id, server_entry)) {
1700     silc_free(server_namec);
1701     silc_free(server_entry->server_name);
1702     silc_free(server_entry->server_info);
1703     silc_free(server_entry);
1704     silc_mutex_unlock(conn->internal->lock);
1705     return NULL;
1706   }
1707
1708   silc_mutex_unlock(conn->internal->lock);
1709   silc_client_ref_server(client, conn, server_entry);
1710
1711   SILC_LOG_DEBUG(("Added %p", server_entry));
1712
1713   return server_entry;
1714 }
1715
1716 /* Removes server from the cache by the server entry. */
1717
1718 SilcBool silc_client_del_server(SilcClient client, SilcClientConnection conn,
1719                                 SilcServerEntry server)
1720 {
1721   SilcBool ret;
1722
1723   if (!server)
1724     return FALSE;
1725
1726   if (silc_atomic_sub_int8(&server->internal.refcnt, 1) > 0)
1727     return FALSE;
1728
1729   SILC_LOG_DEBUG(("Deleting server %p", server));
1730
1731   silc_mutex_lock(conn->internal->lock);
1732   ret = silc_idcache_del_by_context(conn->internal->server_cache,
1733                                     server, NULL);
1734   silc_mutex_unlock(conn->internal->lock);
1735
1736   silc_free(server->server_name);
1737   silc_free(server->server_info);
1738   if (server->public_key)
1739     silc_pkcs_public_key_free(server->public_key);
1740   silc_atomic_uninit8(&server->internal.refcnt);
1741   silc_free(server);
1742
1743   return ret;
1744 }
1745
1746 /* Updates the `server_entry' with the new information sent as argument. */
1747
1748 void silc_client_update_server(SilcClient client,
1749                                SilcClientConnection conn,
1750                                SilcServerEntry server_entry,
1751                                const char *server_name,
1752                                const char *server_info)
1753 {
1754   char *server_namec = NULL;
1755
1756   SILC_LOG_DEBUG(("Updating server %p", server_entry));
1757
1758   if (server_name &&
1759       (!server_entry->server_name ||
1760        !silc_utf8_strcasecmp(server_entry->server_name, server_name))) {
1761
1762     server_namec = silc_identifier_check(server_name, strlen(server_name),
1763                                          SILC_STRING_UTF8, 256, NULL);
1764     if (!server_namec)
1765       return;
1766
1767     silc_free(server_entry->server_name);
1768     server_entry->server_name = strdup(server_name);
1769     if (!server_entry->server_name)
1770       return;
1771
1772     /* Update cache entry */
1773     silc_mutex_lock(conn->internal->lock);
1774     silc_idcache_update_by_context(conn->internal->server_cache, server_entry,
1775                                    NULL, server_namec, TRUE);
1776     silc_mutex_unlock(conn->internal->lock);
1777   }
1778
1779   if (server_info &&
1780       (!server_entry->server_info ||
1781        !silc_utf8_strcasecmp(server_entry->server_info, server_info))) {
1782     silc_free(server_entry->server_info);
1783     server_entry->server_info = strdup(server_info);
1784   }
1785 }
1786
1787 /* Take reference of server entry */
1788
1789 void silc_client_ref_server(SilcClient client, SilcClientConnection conn,
1790                             SilcServerEntry server_entry)
1791 {
1792   silc_atomic_add_int8(&server_entry->internal.refcnt, 1);
1793   SILC_LOG_DEBUG(("Server %p refcnt %d->%d", server_entry,
1794                   silc_atomic_get_int8(&server_entry->internal.refcnt) - 1,
1795                   silc_atomic_get_int8(&server_entry->internal.refcnt)));
1796 }
1797
1798 /* Release reference of server entry */
1799
1800 void silc_client_unref_server(SilcClient client, SilcClientConnection conn,
1801                               SilcServerEntry server_entry)
1802 {
1803   if (server_entry) {
1804     SILC_LOG_DEBUG(("Server %p refcnt %d->%d", server_entry,
1805                     silc_atomic_get_int8(&server_entry->internal.refcnt),
1806                     silc_atomic_get_int8(&server_entry->internal.refcnt)
1807                     - 1));
1808     silc_client_del_server(client, conn, server_entry);
1809   }
1810 }
1811
1812 /* Free server entry list */
1813
1814 void silc_client_list_free_servers(SilcClient client,
1815                                    SilcClientConnection conn,
1816                                    SilcDList server_list)
1817 {
1818   SilcServerEntry server_entry;
1819
1820   if (server_list) {
1821     silc_dlist_start(server_list);
1822     while ((server_entry = silc_dlist_get(server_list)))
1823       silc_client_unref_server(client, conn, server_entry);
1824
1825     silc_dlist_uninit(server_list);
1826   }
1827 }