More client library rewrites (added rekey)
[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"));
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   len = 0;
938   freebase = TRUE;
939   while ((entry = silc_dlist_get(clients))) {
940     if (entry->internal.valid && entry != client_entry)
941       len++;
942     if (entry->internal.valid && entry != client_entry &&
943         silc_utf8_strcasecmp(entry->nickname, client_entry->nickname)) {
944       freebase = FALSE;
945       unformatted = entry;
946     }
947   }
948   if (!len || freebase)
949     return;
950
951   /* If we are changing nickname of our local entry we'll enforce
952      that we will always get the unformatted nickname.  Give our
953      format number to the one that is not formatted now. */
954   if (unformatted && client_entry == conn->local_entry)
955     client_entry = unformatted;
956
957   memset(newnick, 0, sizeof(newnick));
958   cp = client->internal->params->nickname_format;
959   while (cp && *cp) {
960     if (*cp == '%') {
961       cp++;
962       continue;
963     }
964
965     switch(*cp) {
966     case 'n':
967       /* Nickname */
968       if (!client_entry->nickname[0])
969         break;
970       len = strlen(client_entry->nickname);
971       memcpy(&newnick[off], client_entry->nickname, len);
972       off += len;
973       break;
974     case 'h':
975       /* Stripped hostname */
976       if (!client_entry->hostname[0])
977         break;
978       len = strcspn(client_entry->hostname, ".");
979       i = strcspn(client_entry->hostname, "-");
980       if (i < len)
981         len = i;
982       memcpy(&newnick[off], client_entry->hostname, len);
983       off += len;
984       break;
985     case 'H':
986       /* Full hostname */
987       if (!client_entry->hostname[0])
988         break;
989       len = strlen(client_entry->hostname);
990       memcpy(&newnick[off], client_entry->hostname, len);
991       off += len;
992       break;
993     case 's':
994       /* Stripped server name */
995       if (!client_entry->server)
996         break;
997       len = strcspn(client_entry->server, ".");
998       memcpy(&newnick[off], client_entry->server, len);
999       off += len;
1000       break;
1001     case 'S':
1002       /* Full server name */
1003       if (!client_entry->server)
1004         break;
1005       len = strlen(client_entry->server);
1006       memcpy(&newnick[off], client_entry->server, len);
1007       off += len;
1008       break;
1009     case 'a':
1010       /* Ascending number */
1011       {
1012         char tmp[6];
1013         int num, max = 1;
1014
1015         if (silc_dlist_count(clients) == 1)
1016           break;
1017
1018         silc_dlist_start(clients);
1019         while ((entry = silc_dlist_get(clients))) {
1020           if (!silc_utf8_strncasecmp(entry->nickname, newnick, off))
1021             continue;
1022           if (strlen(entry->nickname) <= off)
1023             continue;
1024           num = atoi(&entry->nickname[off]);
1025           if (num > max)
1026             max = num;
1027         }
1028
1029         memset(tmp, 0, sizeof(tmp));
1030         snprintf(tmp, sizeof(tmp) - 1, "%d", ++max);
1031         len = strlen(tmp);
1032         memcpy(&newnick[off], tmp, len);
1033         off += len;
1034       }
1035       break;
1036     default:
1037       /* Some other character in the string */
1038       memcpy(&newnick[off], cp, 1);
1039       off++;
1040       break;
1041     }
1042
1043     cp++;
1044   }
1045
1046   newnick[off] = 0;
1047   memcpy(client_entry->nickname, newnick, strlen(newnick));
1048   silc_client_list_free(client, conn, clients);
1049 }
1050
1051 /************************ Channel Searching Locally *************************/
1052
1053 /* Finds entry for channel by the channel name. Returns the entry or NULL
1054    if the entry was not found. It is found only if the client is joined
1055    to the channel. */
1056
1057 SilcChannelEntry silc_client_get_channel(SilcClient client,
1058                                          SilcClientConnection conn,
1059                                          char *channel)
1060 {
1061   SilcIDCacheEntry id_cache;
1062   SilcChannelEntry entry;
1063
1064   if (!client || !conn || !channel)
1065     return NULL;
1066
1067   SILC_LOG_DEBUG(("Find channel %s", channel));
1068
1069   /* Normalize name for search */
1070   channel = silc_channel_name_check(channel, strlen(channel), SILC_STRING_UTF8,
1071                                     256, NULL);
1072   if (!channel)
1073     return NULL;
1074
1075   silc_mutex_lock(conn->internal->lock);
1076
1077   if (!silc_idcache_find_by_name_one(conn->internal->channel_cache, channel,
1078                                      &id_cache)) {
1079     silc_mutex_unlock(conn->internal->lock);
1080     silc_free(channel);
1081     return NULL;
1082   }
1083
1084   SILC_LOG_DEBUG(("Found"));
1085
1086   entry = id_cache->context;
1087
1088   /* Reference */
1089   silc_client_ref_channel(client, conn, entry);
1090   silc_mutex_unlock(conn->internal->lock);
1091
1092   silc_free(channel);
1093
1094   return entry;
1095 }
1096
1097 /* Finds entry for channel by the channel ID. Returns the entry or NULL
1098    if the entry was not found. It is found only if the client is joined
1099    to the channel. */
1100
1101 SilcChannelEntry silc_client_get_channel_by_id(SilcClient client,
1102                                                SilcClientConnection conn,
1103                                                SilcChannelID *channel_id)
1104 {
1105   SilcIDCacheEntry id_cache;
1106   SilcChannelEntry entry;
1107
1108   if (!client || !conn || !channel_id)
1109     return NULL;
1110
1111   SILC_LOG_DEBUG(("Find channel by id %s",
1112                   silc_id_render(channel_id, SILC_ID_CHANNEL)));
1113
1114   silc_mutex_lock(conn->internal->lock);
1115
1116   if (!silc_idcache_find_by_id_one(conn->internal->channel_cache, channel_id,
1117                                    &id_cache)) {
1118     silc_mutex_unlock(conn->internal->lock);
1119     return NULL;
1120   }
1121
1122   SILC_LOG_DEBUG(("Found"));
1123
1124   entry = id_cache->context;
1125
1126   /* Reference */
1127   silc_client_ref_channel(client, conn, entry);
1128   silc_mutex_unlock(conn->internal->lock);
1129
1130   return entry;
1131 }
1132
1133 /********************** Channel Resolving from Server ***********************/
1134
1135 /* Channel resolving context */
1136 typedef struct {
1137   SilcDList channels;
1138   SilcGetChannelCallback completion;
1139   void *context;
1140 } *SilcClientGetChannelInternal;
1141
1142 /* Resolving command callback */
1143
1144 static SilcBool silc_client_get_channel_cb(SilcClient client,
1145                                            SilcClientConnection conn,
1146                                            SilcCommand command,
1147                                            SilcStatus status,
1148                                            SilcStatus error,
1149                                            void *context,
1150                                            va_list ap)
1151 {
1152   SilcClientGetChannelInternal i = context;
1153   SilcChannelEntry entry;
1154
1155   if (error != SILC_STATUS_OK) {
1156     SILC_LOG_DEBUG(("Resolving failed: %s", silc_get_status_message(error)));
1157     if (i->completion)
1158       i->completion(client, conn, error, NULL, i->context);
1159     goto out;
1160   }
1161
1162   /* Add the returned channel to list */
1163   if (i->completion) {
1164     entry = va_arg(ap, SilcChannelEntry);
1165     silc_client_ref_channel(client, conn, entry);
1166     silc_dlist_add(i->channels, entry);
1167   }
1168
1169   if (status == SILC_STATUS_OK || status == SILC_STATUS_LIST_END) {
1170     /* Deliver the channels to the caller */
1171     if (i->completion) {
1172       SILC_LOG_DEBUG(("Resolved %d channels", silc_dlist_count(i->channels)));
1173       silc_dlist_start(i->channels);
1174       i->completion(client, conn, SILC_STATUS_OK, i->channels, i->context);
1175     }
1176     goto out;
1177   }
1178
1179   return TRUE;
1180
1181  out:
1182   silc_client_list_free_channels(client, conn, i->channels);
1183   silc_free(i);
1184   return FALSE;
1185 }
1186
1187 /* Resolves channel entry from the server by the channel name. */
1188
1189 void silc_client_get_channel_resolve(SilcClient client,
1190                                      SilcClientConnection conn,
1191                                      char *channel_name,
1192                                      SilcGetChannelCallback completion,
1193                                      void *context)
1194 {
1195   SilcClientGetChannelInternal i;
1196
1197   if (!client || !conn || !channel_name || !completion)
1198     return;
1199
1200   SILC_LOG_DEBUG(("Resolve channel %s", channel_name));
1201
1202   i = silc_calloc(1, sizeof(*i));
1203   if (!i)
1204     return;
1205   i->completion = completion;
1206   i->context = context;
1207   i->channels = silc_dlist_init();
1208   if (!i->channels) {
1209     silc_free(i);
1210     return;
1211   }
1212
1213   /* Send the command */
1214   if (!silc_client_command_send(client, conn, SILC_COMMAND_IDENTIFY,
1215                                 silc_client_get_channel_cb, i, 1,
1216                                 3, channel_name, strlen(channel_name))) {
1217     if (completion)
1218       completion(client, conn, SILC_STATUS_ERR_RESOURCE_LIMIT, NULL, context);
1219   }
1220 }
1221
1222 /* Resolves channel information from the server by the channel ID. */
1223
1224 SilcUInt16
1225 silc_client_get_channel_by_id_resolve(SilcClient client,
1226                                       SilcClientConnection conn,
1227                                       SilcChannelID *channel_id,
1228                                       SilcGetChannelCallback completion,
1229                                       void *context)
1230 {
1231   SilcClientGetChannelInternal i;
1232   SilcBuffer idp;
1233   SilcUInt16 cmd_ident;
1234
1235   if (!client || !conn || !channel_id || !completion)
1236     return 0;
1237
1238   SILC_LOG_DEBUG(("Resolve channel by id %s",
1239                   silc_id_render(channel_id, SILC_ID_CHANNEL)));
1240
1241   i = silc_calloc(1, sizeof(*i));
1242   if (!i)
1243     return 0;
1244   i->completion = completion;
1245   i->context = context;
1246   i->channels = silc_dlist_init();
1247   if (!i->channels) {
1248     silc_free(i);
1249     return 0;
1250   }
1251
1252   /* Send the command */
1253   idp = silc_id_payload_encode(channel_id, SILC_ID_CHANNEL);
1254   cmd_ident = silc_client_command_send(client, conn, SILC_COMMAND_IDENTIFY,
1255                                        silc_client_get_channel_cb, i, 1,
1256                                        5, silc_buffer_datalen(idp));
1257   silc_buffer_free(idp);
1258   if (!cmd_ident && completion)
1259     completion(client, conn, SILC_STATUS_ERR_RESOURCE_LIMIT, NULL, context);
1260
1261   return cmd_ident;
1262 }
1263
1264 /************************* Channel Entry Routines ***************************/
1265
1266 /* Add new channel entry to the ID Cache */
1267
1268 SilcChannelEntry silc_client_add_channel(SilcClient client,
1269                                          SilcClientConnection conn,
1270                                          const char *channel_name,
1271                                          SilcUInt32 mode,
1272                                          SilcChannelID *channel_id)
1273 {
1274   SilcChannelEntry channel;
1275   char *channel_namec;
1276
1277   SILC_LOG_DEBUG(("Start"));
1278
1279   channel = silc_calloc(1, sizeof(*channel));
1280   if (!channel)
1281     return NULL;
1282
1283   silc_atomic_init8(&channel->internal.refcnt, 0);
1284   channel->id = *channel_id;
1285   channel->mode = mode;
1286
1287   channel->channel_name = strdup(channel_name);
1288   if (!channel->channel_name) {
1289     silc_free(channel);
1290     return NULL;
1291   }
1292
1293   channel->user_list = silc_hash_table_alloc(1, silc_hash_ptr, NULL, NULL,
1294                                              NULL, NULL, NULL, TRUE);
1295   if (!channel->user_list) {
1296     silc_free(channel->channel_name);
1297     silc_free(channel);
1298     return NULL;
1299   }
1300
1301   /* Normalize channel name */
1302   channel_namec = silc_channel_name_check(channel_name, strlen(channel_name),
1303                                           SILC_STRING_UTF8, 256, NULL);
1304   if (!channel_namec) {
1305     silc_free(channel->channel_name);
1306     silc_hash_table_free(channel->user_list);
1307     silc_free(channel);
1308     return NULL;
1309   }
1310
1311   silc_mutex_lock(conn->internal->lock);
1312
1313   /* Add channel to cache, the normalized channel name is saved to cache */
1314   if (!silc_idcache_add(conn->internal->channel_cache, channel_namec,
1315                         &channel->id, channel)) {
1316     silc_free(channel_namec);
1317     silc_free(channel->channel_name);
1318     silc_hash_table_free(channel->user_list);
1319     silc_free(channel);
1320     silc_mutex_unlock(conn->internal->lock);
1321     return NULL;
1322   }
1323
1324   silc_mutex_unlock(conn->internal->lock);
1325   silc_client_ref_channel(client, conn, channel);
1326
1327   SILC_LOG_DEBUG(("Added"));
1328
1329   return channel;
1330 }
1331
1332 /* Removes channel from the cache by the channel entry. */
1333
1334 SilcBool silc_client_del_channel(SilcClient client, SilcClientConnection conn,
1335                                  SilcChannelEntry channel)
1336 {
1337   SilcBool ret;
1338   SilcCipher key;
1339   SilcHmac hmac;
1340
1341   if (!channel)
1342     return FALSE;
1343
1344   if (silc_atomic_sub_int8(&channel->internal.refcnt, 1) > 0)
1345     return FALSE;
1346
1347   SILC_LOG_DEBUG(("Deleting channel %p", channel));
1348
1349   silc_mutex_lock(conn->internal->lock);
1350   ret = silc_idcache_del_by_context(conn->internal->channel_cache,
1351                                     channel, NULL);
1352   silc_mutex_unlock(conn->internal->lock);
1353
1354   if (!ret)
1355     return FALSE;
1356
1357   silc_client_empty_channel(client, conn, channel);
1358   silc_hash_table_free(channel->user_list);
1359   silc_free(channel->channel_name);
1360   silc_free(channel->topic);
1361   if (channel->founder_key)
1362     silc_pkcs_public_key_free(channel->founder_key);
1363   if (channel->internal.channel_key)
1364     silc_cipher_free(channel->internal.channel_key);
1365   if (channel->internal.hmac)
1366     silc_hmac_free(channel->internal.hmac);
1367   if (channel->internal.old_channel_keys) {
1368     silc_dlist_start(channel->internal.old_channel_keys);
1369     while ((key = silc_dlist_get(channel->internal.old_channel_keys)))
1370       silc_cipher_free(key);
1371     silc_dlist_uninit(channel->internal.old_channel_keys);
1372   }
1373   if (channel->internal.old_hmacs) {
1374     silc_dlist_start(channel->internal.old_hmacs);
1375     while ((hmac = silc_dlist_get(channel->internal.old_hmacs)))
1376       silc_hmac_free(hmac);
1377     silc_dlist_uninit(channel->internal.old_hmacs);
1378   }
1379   silc_client_del_channel_private_keys(client, conn, channel);
1380   silc_atomic_uninit8(&channel->internal.refcnt);
1381   silc_schedule_task_del_by_context(conn->client->schedule, channel);
1382   silc_free(channel);
1383
1384   return ret;
1385 }
1386
1387 /* Replaces the channel ID of the `channel' to `new_id'. Returns FALSE
1388    if the ID could not be changed. */
1389
1390 SilcBool silc_client_replace_channel_id(SilcClient client,
1391                                         SilcClientConnection conn,
1392                                         SilcChannelEntry channel,
1393                                         SilcChannelID *new_id)
1394 {
1395   SilcBool ret = FALSE;
1396
1397   if (!new_id)
1398     return FALSE;
1399
1400   SILC_LOG_DEBUG(("Old Channel ID id(%s)",
1401                   silc_id_render(&channel->id, SILC_ID_CHANNEL)));
1402   SILC_LOG_DEBUG(("New Channel ID id(%s)",
1403                   silc_id_render(new_id, SILC_ID_CHANNEL)));
1404
1405   /* Update the ID */
1406   silc_mutex_lock(conn->internal->lock);
1407   silc_idcache_update_by_context(conn->internal->channel_cache, channel,
1408                                  new_id, NULL, FALSE);
1409   silc_mutex_unlock(conn->internal->lock);
1410
1411   return ret;
1412 }
1413
1414 /* Take reference of channel entry */
1415
1416 void silc_client_ref_channel(SilcClient client, SilcClientConnection conn,
1417                              SilcChannelEntry channel_entry)
1418 {
1419   silc_atomic_add_int8(&channel_entry->internal.refcnt, 1);
1420   SILC_LOG_DEBUG(("Channel %p refcnt %d->%d", channel_entry,
1421                   silc_atomic_get_int8(&channel_entry->internal.refcnt) - 1,
1422                   silc_atomic_get_int8(&channel_entry->internal.refcnt)));
1423 }
1424
1425 /* Release reference of channel entry */
1426
1427 void silc_client_unref_channel(SilcClient client, SilcClientConnection conn,
1428                                SilcChannelEntry channel_entry)
1429 {
1430   if (channel_entry) {
1431     SILC_LOG_DEBUG(("Channel %p refcnt %d->%d", channel_entry,
1432                     silc_atomic_get_int8(&channel_entry->internal.refcnt),
1433                     silc_atomic_get_int8(&channel_entry->internal.refcnt)
1434                     - 1));
1435     silc_client_del_channel(client, conn, channel_entry);
1436   }
1437 }
1438
1439 /* Free channel entry list */
1440
1441 void silc_client_list_free_channels(SilcClient client,
1442                                     SilcClientConnection conn,
1443                                     SilcDList channel_list)
1444 {
1445   SilcChannelEntry channel_entry;
1446
1447   if (channel_list) {
1448     silc_dlist_start(channel_list);
1449     while ((channel_entry = silc_dlist_get(channel_list)))
1450       silc_client_unref_channel(client, conn, channel_entry);
1451
1452     silc_dlist_uninit(channel_list);
1453   }
1454 }
1455
1456 /************************* Server Searching Locally *************************/
1457
1458 /* Finds entry for server by the server name. */
1459
1460 SilcServerEntry silc_client_get_server(SilcClient client,
1461                                        SilcClientConnection conn,
1462                                        char *server_name)
1463 {
1464   SilcIDCacheEntry id_cache;
1465   SilcServerEntry entry;
1466
1467   if (!client || !conn || !server_name)
1468     return NULL;
1469
1470   SILC_LOG_DEBUG(("Find server by name %s", server_name));
1471
1472   /* Normalize server name for search */
1473   server_name = silc_identifier_check(server_name, strlen(server_name),
1474                                       SILC_STRING_UTF8, 256, NULL);
1475   if (!server_name)
1476     return NULL;
1477
1478   silc_mutex_lock(conn->internal->lock);
1479
1480   if (!silc_idcache_find_by_name_one(conn->internal->server_cache,
1481                                      server_name, &id_cache)) {
1482     silc_free(server_name);
1483     silc_mutex_unlock(conn->internal->lock);
1484     return NULL;
1485   }
1486
1487   SILC_LOG_DEBUG(("Found"));
1488
1489   /* Reference */
1490   entry = id_cache->context;
1491   silc_client_ref_server(client, conn, entry);
1492
1493   silc_mutex_unlock(conn->internal->lock);
1494
1495   silc_free(server_name);
1496
1497   return entry;
1498 }
1499
1500 /* Finds entry for server by the server ID. */
1501
1502 SilcServerEntry silc_client_get_server_by_id(SilcClient client,
1503                                              SilcClientConnection conn,
1504                                              SilcServerID *server_id)
1505 {
1506   SilcIDCacheEntry id_cache;
1507   SilcServerEntry entry;
1508
1509   if (!client || !conn || !server_id)
1510     return NULL;
1511
1512   SILC_LOG_DEBUG(("Find server by id %s",
1513                   silc_id_render(server_id, SILC_ID_SERVER)));
1514
1515   silc_mutex_lock(conn->internal->lock);
1516
1517   if (!silc_idcache_find_by_id_one(conn->internal->server_cache,
1518                                    server_id, &id_cache)) {
1519     silc_mutex_unlock(conn->internal->lock);
1520     return NULL;
1521   }
1522
1523   SILC_LOG_DEBUG(("Found"));
1524
1525   /* Reference */
1526   entry = id_cache->context;
1527   silc_client_ref_server(client, conn, entry);
1528
1529   silc_mutex_unlock(conn->internal->lock);
1530
1531   return entry;
1532 }
1533
1534 /*********************** Server Resolving from Server ***********************/
1535
1536 /* Resolving context */
1537 typedef struct {
1538   SilcDList servers;
1539   SilcGetServerCallback completion;
1540   void *context;
1541 } *SilcClientGetServerInternal;
1542
1543 /* Resolving command callback */
1544
1545 static SilcBool silc_client_get_server_cb(SilcClient client,
1546                                           SilcClientConnection conn,
1547                                           SilcCommand command,
1548                                           SilcStatus status,
1549                                           SilcStatus error,
1550                                           void *context,
1551                                           va_list ap)
1552 {
1553   SilcClientGetServerInternal i = context;
1554   SilcServerEntry server;
1555
1556   if (error != SILC_STATUS_OK) {
1557     SILC_LOG_DEBUG(("Resolving failed: %s", silc_get_status_message(error)));
1558     if (i->completion)
1559       i->completion(client, conn, error, NULL, i->context);
1560     goto out;
1561   }
1562
1563   /* Add the returned servers to list */
1564   if (i->completion) {
1565     server = va_arg(ap, SilcServerEntry);
1566     silc_client_ref_server(client, conn, server);
1567     silc_dlist_add(i->servers, server);
1568     server->internal.resolve_cmd_ident = 0;
1569   }
1570
1571   if (status == SILC_STATUS_OK || status == SILC_STATUS_LIST_END) {
1572     /* Deliver the servers to the caller */
1573     if (i->completion) {
1574       SILC_LOG_DEBUG(("Resolved %d servers", silc_dlist_count(i->servers)));
1575       silc_dlist_start(i->servers);
1576       i->completion(client, conn, SILC_STATUS_OK, i->servers, i->context);
1577     }
1578     goto out;
1579   }
1580
1581   return TRUE;
1582
1583  out:
1584   silc_client_list_free_servers(client, conn, i->servers);
1585   silc_free(i);
1586   return FALSE;
1587 }
1588
1589 /* Resolve server by server ID */
1590
1591 SilcUInt16
1592 silc_client_get_server_by_id_resolve(SilcClient client,
1593                                      SilcClientConnection conn,
1594                                      SilcServerID *server_id,
1595                                      SilcGetServerCallback completion,
1596                                      void *context)
1597 {
1598   SilcClientGetServerInternal i;
1599   SilcServerEntry server;
1600   SilcBuffer idp;
1601   SilcUInt16 cmd_ident;
1602
1603   if (!client || !conn || !server_id || !completion)
1604     return 0;
1605
1606   SILC_LOG_DEBUG(("Resolve server by id %s",
1607                   silc_id_render(server_id, SILC_ID_SERVER)));
1608
1609   i = silc_calloc(1, sizeof(*i));
1610   if (!i)
1611     return 0;
1612   i->completion = completion;
1613   i->context = context;
1614   i->servers = silc_dlist_init();
1615   if (!i->servers) {
1616     silc_free(i);
1617     return 0;
1618   }
1619
1620   /* Attach to resolving, if on going */
1621   server = silc_client_get_server_by_id(client, conn, server_id);
1622   if (server && server->internal.resolve_cmd_ident) {
1623     SILC_LOG_DEBUG(("Attach to existing resolving"));
1624     silc_client_unref_server(client, conn, server);
1625     silc_client_command_pending(conn, SILC_COMMAND_NONE,
1626                                 server->internal.resolve_cmd_ident,
1627                                 silc_client_get_server_cb, i);
1628     return server->internal.resolve_cmd_ident;
1629   }
1630
1631   /* Send the command */
1632   idp = silc_id_payload_encode(server_id, SILC_ID_SERVER);
1633   cmd_ident = silc_client_command_send(client, conn, SILC_COMMAND_IDENTIFY,
1634                                        silc_client_get_server_cb, i, 1,
1635                                        5, silc_buffer_datalen(idp));
1636   silc_buffer_free(idp);
1637   if (!cmd_ident && completion)
1638     completion(client, conn, SILC_STATUS_ERR_RESOURCE_LIMIT, NULL, context);
1639
1640   if (server && cmd_ident)
1641     server->internal.resolve_cmd_ident = cmd_ident;
1642
1643   silc_client_unref_server(client, conn, server);
1644
1645   return cmd_ident;
1646 }
1647
1648 /************************** Server Entry Routines ***************************/
1649
1650 /* Add new server entry */
1651
1652 SilcServerEntry silc_client_add_server(SilcClient client,
1653                                        SilcClientConnection conn,
1654                                        const char *server_name,
1655                                        const char *server_info,
1656                                        SilcServerID *server_id)
1657 {
1658   SilcServerEntry server_entry;
1659   char *server_namec = NULL;
1660
1661   if (!server_id)
1662     return NULL;
1663
1664   SILC_LOG_DEBUG(("Adding new server %s", server_name));
1665
1666   server_entry = silc_calloc(1, sizeof(*server_entry));
1667   if (!server_entry)
1668     return NULL;
1669
1670   silc_atomic_init8(&server_entry->internal.refcnt, 0);
1671   server_entry->id = *server_id;
1672   if (server_name)
1673     server_entry->server_name = strdup(server_name);
1674   if (server_info)
1675     server_entry->server_info = strdup(server_info);
1676
1677   /* Normalize server name */
1678   if (server_name) {
1679     server_namec = silc_identifier_check(server_name, strlen(server_name),
1680                                          SILC_STRING_UTF8, 256, NULL);
1681     if (!server_namec) {
1682       silc_free(server_entry->server_name);
1683       silc_free(server_entry->server_info);
1684       silc_free(server_entry);
1685       return NULL;
1686     }
1687   }
1688
1689   silc_mutex_lock(conn->internal->lock);
1690
1691   /* Add server to cache */
1692   if (!silc_idcache_add(conn->internal->server_cache, server_namec,
1693                         &server_entry->id, server_entry)) {
1694     silc_free(server_namec);
1695     silc_free(server_entry->server_name);
1696     silc_free(server_entry->server_info);
1697     silc_free(server_entry);
1698     silc_mutex_unlock(conn->internal->lock);
1699     return NULL;
1700   }
1701
1702   silc_mutex_unlock(conn->internal->lock);
1703   silc_client_ref_server(client, conn, server_entry);
1704
1705   SILC_LOG_DEBUG(("Added"));
1706
1707   return server_entry;
1708 }
1709
1710 /* Removes server from the cache by the server entry. */
1711
1712 SilcBool silc_client_del_server(SilcClient client, SilcClientConnection conn,
1713                                 SilcServerEntry server)
1714 {
1715   SilcBool ret;
1716
1717   if (!server)
1718     return FALSE;
1719
1720   if (silc_atomic_sub_int8(&server->internal.refcnt, 1) > 0)
1721     return FALSE;
1722
1723   SILC_LOG_DEBUG(("Deleting server %p", server));
1724
1725   silc_mutex_lock(conn->internal->lock);
1726   ret = silc_idcache_del_by_context(conn->internal->server_cache,
1727                                     server, NULL);
1728   silc_mutex_unlock(conn->internal->lock);
1729
1730   silc_free(server->server_name);
1731   silc_free(server->server_info);
1732   if (server->public_key)
1733     silc_pkcs_public_key_free(server->public_key);
1734   silc_atomic_uninit8(&server->internal.refcnt);
1735   silc_free(server);
1736
1737   return ret;
1738 }
1739
1740 /* Updates the `server_entry' with the new information sent as argument. */
1741
1742 void silc_client_update_server(SilcClient client,
1743                                SilcClientConnection conn,
1744                                SilcServerEntry server_entry,
1745                                const char *server_name,
1746                                const char *server_info)
1747 {
1748   char *server_namec = NULL;
1749
1750   SILC_LOG_DEBUG(("Updating server %p", server_entry));
1751
1752   if (server_name &&
1753       (!server_entry->server_name ||
1754        !silc_utf8_strcasecmp(server_entry->server_name, server_name))) {
1755
1756     server_namec = silc_identifier_check(server_name, strlen(server_name),
1757                                          SILC_STRING_UTF8, 256, NULL);
1758     if (!server_namec)
1759       return;
1760
1761     silc_free(server_entry->server_name);
1762     server_entry->server_name = strdup(server_name);
1763     if (!server_entry->server_name)
1764       return;
1765
1766     /* Update cache entry */
1767     silc_mutex_lock(conn->internal->lock);
1768     silc_idcache_update_by_context(conn->internal->server_cache, server_entry,
1769                                    NULL, server_namec, TRUE);
1770     silc_mutex_unlock(conn->internal->lock);
1771   }
1772
1773   if (server_info &&
1774       (!server_entry->server_info ||
1775        !silc_utf8_strcasecmp(server_entry->server_info, server_info))) {
1776     silc_free(server_entry->server_info);
1777     server_entry->server_info = strdup(server_info);
1778   }
1779 }
1780
1781 /* Take reference of server entry */
1782
1783 void silc_client_ref_server(SilcClient client, SilcClientConnection conn,
1784                             SilcServerEntry server_entry)
1785 {
1786   silc_atomic_add_int8(&server_entry->internal.refcnt, 1);
1787 }
1788
1789 /* Release reference of server entry */
1790
1791 void silc_client_unref_server(SilcClient client, SilcClientConnection conn,
1792                               SilcServerEntry server_entry)
1793 {
1794   silc_client_del_server(client, conn, server_entry);
1795 }
1796
1797 /* Free server entry list */
1798
1799 void silc_client_list_free_servers(SilcClient client,
1800                                    SilcClientConnection conn,
1801                                    SilcDList server_list)
1802 {
1803   SilcServerEntry server_entry;
1804
1805   if (server_list) {
1806     silc_dlist_start(server_list);
1807     while ((server_entry = silc_dlist_get(server_list)))
1808       silc_client_unref_server(client, conn, server_entry);
1809
1810     silc_dlist_uninit(server_list);
1811   }
1812 }