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