Added preliminary Symbian support.
[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   va_list tmp;
427   int i;
428
429   SILC_LOG_DEBUG(("Resolve clients from Client ID list"));
430
431   if (!client || !conn || !client_id_list)
432     return 0;
433
434   in = silc_calloc(1, sizeof(*in));
435   if (!in)
436     return 0;
437   in->completion = completion;
438   in->context = context;
439   in->list_count = list_count;
440   in->client_id_list = silc_buffer_copy(client_id_list);
441   if (!in->client_id_list)
442     goto err;
443
444   for (i = 0; i < list_count; i++) {
445     /* Get Client ID */
446     SILC_GET16_MSB(idp_len, client_id_list->data + 2);
447     idp_len += 4;
448     if (!silc_id_payload_parse_id(client_id_list->data, idp_len, &id))
449       goto err;
450
451     /* Check if we have this client cached already.  If we don't have the
452        entry or it has incomplete info, then resolve it from the server. */
453     entry = silc_client_get_client_by_id(client, conn, &id.u.client_id);
454     if (!entry || !entry->nickname[0] || !entry->username[0] ||
455         !entry->realname) {
456       if (!res_argv) {
457         res_argv = silc_calloc(list_count, sizeof(*res_argv));
458         res_argv_lens = silc_calloc(list_count, sizeof(*res_argv_lens));
459         res_argv_types = silc_calloc(list_count, sizeof(*res_argv_types));
460         if (!res_argv || !res_argv_lens || !res_argv_types) {
461           silc_client_unref_client(client, conn, entry);
462           goto err;
463         }
464       }
465
466       res_argv[res_argc] = client_id_list->data;
467       res_argv_lens[res_argc] = idp_len;
468       res_argv_types[res_argc] = res_argc + 4;
469       res_argc++;
470     }
471     silc_client_unref_client(client, conn, entry);
472
473     if (!silc_buffer_pull(client_id_list, idp_len))
474       goto err;
475   }
476   silc_buffer_start(client_id_list);
477
478   /* Query the unknown client information from server */
479   if (res_argc) {
480     cmd_ident = silc_client_command_send_argv(client,
481                                               conn, SILC_COMMAND_WHOIS,
482                                               silc_client_get_clients_list_cb,
483                                               in, res_argc, res_argv,
484                                               res_argv_lens,
485                                               res_argv_types);
486     silc_free(res_argv);
487     silc_free(res_argv_lens);
488     silc_free(res_argv_types);
489     return cmd_ident;
490   }
491
492   /* We have the clients in cache, get them and call the completion */
493   silc_client_get_clients_list_cb(client, conn, SILC_COMMAND_WHOIS,
494                                   SILC_STATUS_OK, SILC_STATUS_OK, in, tmp);
495   return 0;
496
497  err:
498   silc_buffer_free(in->client_id_list);
499   silc_free(in);
500   silc_free(res_argv);
501   silc_free(res_argv_lens);
502   silc_free(res_argv_types);
503   return 0;
504 }
505
506 #if 0
507 typedef struct {
508   SilcClient client;
509   SilcClientConnection conn;
510   SilcChannelID channel_id;
511   SilcGetClientCallback completion;
512   void *context;
513   int res_count;
514 } *GetClientsByChannelInternal;
515
516 SILC_CLIENT_CMD_FUNC(get_clients_by_channel_cb)
517 {
518   GetClientsByChannelInternal i = context;
519   SilcClientEntry *clients = NULL;
520   SilcUInt32 clients_count = 0;
521   SilcBool found = FALSE;
522   SilcChannelEntry channel;
523   SilcHashTableList htl;
524   SilcChannelUser chu;
525
526   if (i->res_count) {
527     i->res_count--;
528     if (i->res_count)
529       return;
530   }
531
532   channel = silc_client_get_channel_by_id(i->client, i->conn, &i->channel_id);
533   if (channel && !silc_hash_table_count(channel->user_list)) {
534     clients = silc_calloc(silc_hash_table_count(channel->user_list),
535                           sizeof(*clients));
536     silc_hash_table_list(channel->user_list, &htl);
537     while (silc_hash_table_get(&htl, NULL, (void *)&chu))
538       clients[clients_count++] = chu->client;
539     silc_hash_table_list_reset(&htl);
540     found = TRUE;
541   }
542
543   if (found) {
544     i->completion(i->client, i->conn, clients, clients_count, i->context);
545     silc_free(clients);
546   } else {
547     i->completion(i->client, i->conn, NULL, 0, i->context);
548   }
549
550   silc_free(i);
551 }
552
553 /* Gets client entries by the channel entry indicated by `channel'.  Thus,
554    it resolves the clients currently on that channel. */
555
556 void silc_client_get_clients_by_channel(SilcClient client,
557                                         SilcClientConnection conn,
558                                         SilcChannelEntry channel,
559                                         SilcGetClientCallback completion,
560                                         void *context)
561 {
562   GetClientsByChannelInternal in;
563   SilcHashTableList htl;
564   SilcChannelUser chu;
565   SilcClientEntry entry;
566   unsigned char **res_argv = NULL;
567   SilcUInt32 *res_argv_lens = NULL, *res_argv_types = NULL, res_argc = 0;
568   SilcBuffer idp;
569   SilcBool wait_res = FALSE;
570
571   assert(client && conn && channel);
572
573   SILC_LOG_DEBUG(("Start"));
574
575   in = silc_calloc(1, sizeof(*in));
576   in->client = client;
577   in->conn = conn;
578   in->channel_id = *channel->id;
579   in->completion = completion;
580   in->context = context;
581
582   /* If user list does not exist, send USERS command. */
583   if (!channel->user_list || !silc_hash_table_count(channel->user_list)) {
584     SILC_LOG_DEBUG(("Sending USERS"));
585     silc_client_command_register(client, SILC_COMMAND_USERS, NULL, NULL,
586                                  silc_client_command_reply_users_i, 0,
587                                  ++conn->cmd_ident);
588     silc_client_command_send(client, conn, SILC_COMMAND_USERS,
589                              conn->cmd_ident, 1, 2, channel->channel_name,
590                              strlen(channel->channel_name));
591     silc_client_command_pending(conn, SILC_COMMAND_USERS, conn->cmd_ident,
592                                 silc_client_command_get_clients_by_channel_cb,
593                                 in);
594     return;
595   }
596
597   silc_hash_table_list(channel->user_list, &htl);
598   while (silc_hash_table_get(&htl, NULL, (void *)&chu)) {
599     entry = chu->client;
600
601     /* If the entry has incomplete info, then resolve it from the server. */
602     if (!entry->nickname[0] || !entry->realname) {
603       if (entry->status & SILC_CLIENT_STATUS_RESOLVING) {
604         /* Attach to this resolving and wait until it finishes */
605         silc_client_command_pending(
606                             conn, SILC_COMMAND_NONE,
607                             entry->resolve_cmd_ident,
608                             silc_client_command_get_clients_by_channel_cb,
609                             (void *)in);
610         wait_res = TRUE;
611         in->res_count++;
612         continue;
613       }
614       entry->status |= SILC_CLIENT_STATUS_RESOLVING;
615       entry->resolve_cmd_ident = conn->cmd_ident + 1;
616
617       idp = silc_id_payload_encode(entry->id, SILC_ID_CLIENT);
618
619       /* No we don't have it, query it from the server. Assemble argument
620          table that will be sent for the WHOIS command later. */
621       res_argv = silc_realloc(res_argv, sizeof(*res_argv) *
622                               (res_argc + 1));
623       res_argv_lens = silc_realloc(res_argv_lens, sizeof(*res_argv_lens) *
624                                    (res_argc + 1));
625       res_argv_types = silc_realloc(res_argv_types, sizeof(*res_argv_types) *
626                                     (res_argc + 1));
627       res_argv[res_argc] = silc_memdup(idp->data, idp->len);
628       res_argv_lens[res_argc] = idp->len;
629       res_argv_types[res_argc] = res_argc + 4;
630       res_argc++;
631
632       silc_buffer_free(idp);
633     }
634   }
635   silc_hash_table_list_reset(&htl);
636
637   /* Query the client information from server if the list included clients
638      that we don't know about. */
639   if (res_argc) {
640     SilcBuffer res_cmd;
641
642     /* Send the WHOIS command to server */
643     res_cmd = silc_command_payload_encode(SILC_COMMAND_WHOIS,
644                                           res_argc, res_argv, res_argv_lens,
645                                           res_argv_types, ++conn->cmd_ident);
646     silc_client_packet_send(client, conn->sock, SILC_PACKET_COMMAND,
647                             NULL, 0, NULL, NULL, res_cmd->data, res_cmd->len,
648                             TRUE);
649
650     /* Register our own command reply for this command */
651     silc_client_command_register(client, SILC_COMMAND_WHOIS, NULL, NULL,
652                                  silc_client_command_reply_whois_i, 0,
653                                  conn->cmd_ident);
654
655     /* Process the applications request after reply has been received  */
656     silc_client_command_pending(
657                            conn, SILC_COMMAND_WHOIS, conn->cmd_ident,
658                            silc_client_command_get_clients_by_channel_cb,
659                            (void *)in);
660     in->res_count++;
661
662     silc_buffer_free(res_cmd);
663     silc_free(res_argv);
664     silc_free(res_argv_lens);
665     silc_free(res_argv_types);
666     return;
667   }
668
669   if (wait_res)
670     return;
671
672   /* We have the clients in cache, get them and call the completion */
673   silc_client_command_get_clients_by_channel_cb((void *)in, NULL);
674 }
675 #endif /* 0 */
676
677
678 /************************** Client Entry Routines ***************************/
679
680 /* Creates new client entry and adds it to the ID cache. Returns pointer
681    to the new entry. */
682
683 SilcClientEntry silc_client_add_client(SilcClient client,
684                                        SilcClientConnection conn,
685                                        char *nickname, char *username,
686                                        char *userinfo, SilcClientID *id,
687                                        SilcUInt32 mode)
688 {
689   SilcClientEntry client_entry;
690   char *nick = NULL;
691
692   SILC_LOG_DEBUG(("Adding new client entry"));
693
694   /* Save the client infos */
695   client_entry = silc_calloc(1, sizeof(*client_entry));
696   if (!client_entry)
697     return NULL;
698
699   silc_atomic_init8(&client_entry->internal.refcnt, 0);
700   client_entry->id = *id;
701   client_entry->internal.valid = TRUE;
702   client_entry->mode = mode;
703   client_entry->realname = userinfo ? strdup(userinfo) : NULL;
704   silc_parse_userfqdn(nickname, client_entry->nickname,
705                       sizeof(client_entry->nickname),
706                       client_entry->server,
707                       sizeof(client_entry->server));
708   silc_parse_userfqdn(username, client_entry->username,
709                       sizeof(client_entry->username),
710                       client_entry->hostname,
711                       sizeof(client_entry->hostname));
712   client_entry->channels = silc_hash_table_alloc(1, silc_hash_ptr, NULL, NULL,
713                                                  NULL, NULL, NULL, TRUE);
714   if (!client_entry->channels) {
715     silc_free(client_entry->realname);
716     silc_free(client_entry);
717     return NULL;
718   }
719
720   /* Normalize nickname */
721   if (client_entry->nickname[0]) {
722     nick = silc_identifier_check(client_entry->nickname,
723                                  strlen(client_entry->nickname),
724                                  SILC_STRING_UTF8, 128, NULL);
725     if (!nick) {
726       silc_free(client_entry->realname);
727       silc_hash_table_free(client_entry->channels);
728       silc_free(client_entry);
729       return NULL;
730     }
731   }
732
733   /* Format the nickname */
734   silc_client_nickname_format(client, conn, client_entry, FALSE);
735
736   silc_mutex_lock(conn->internal->lock);
737
738   /* Add client to cache, the normalized nickname is saved to cache */
739   if (!silc_idcache_add(conn->internal->client_cache, nick,
740                         &client_entry->id, client_entry)) {
741     silc_free(nick);
742     silc_free(client_entry->realname);
743     silc_hash_table_free(client_entry->channels);
744     silc_free(client_entry);
745     silc_mutex_unlock(conn->internal->lock);
746     return NULL;
747   }
748
749   client_entry->nickname_normalized = nick;
750
751   silc_mutex_unlock(conn->internal->lock);
752   silc_client_ref_client(client, conn, client_entry);
753
754   SILC_LOG_DEBUG(("Added %p", client_entry));
755
756   return client_entry;
757 }
758
759 /* Updates the `client_entry' with the new information sent as argument. */
760
761 void silc_client_update_client(SilcClient client,
762                                SilcClientConnection conn,
763                                SilcClientEntry client_entry,
764                                const char *nickname,
765                                const char *username,
766                                const char *userinfo,
767                                SilcUInt32 mode)
768 {
769   char *nick = NULL;
770
771   SILC_LOG_DEBUG(("Update client entry"));
772
773   if (!client_entry->realname && userinfo)
774     client_entry->realname = strdup(userinfo);
775   if ((!client_entry->username[0] || !client_entry->hostname[0]) && username)
776     silc_parse_userfqdn(username, client_entry->username,
777                         sizeof(client_entry->username),
778                         client_entry->hostname,
779                         sizeof(client_entry->username));
780   if (!client_entry->nickname[0] && nickname) {
781     silc_parse_userfqdn(nickname, client_entry->nickname,
782                         sizeof(client_entry->nickname),
783                         client_entry->server,
784                         sizeof(client_entry->server));
785
786     /* Normalize nickname */
787     nick = silc_identifier_check(client_entry->nickname,
788                                  strlen(client_entry->nickname),
789                                  SILC_STRING_UTF8, 128, NULL);
790     if (!nick)
791       return;
792
793     /* Format nickname */
794     silc_client_nickname_format(client, conn, client_entry,
795                                 client_entry == conn->local_entry);
796
797     /* Update cache entry */
798     silc_mutex_lock(conn->internal->lock);
799     silc_idcache_update_by_context(conn->internal->client_cache,
800                                    client_entry, NULL, nick, TRUE);
801     silc_mutex_unlock(conn->internal->lock);
802     client_entry->nickname_normalized = nick;
803   }
804   client_entry->mode = mode;
805 }
806
807 /* Change a client's nickname */
808
809 SilcBool silc_client_change_nickname(SilcClient client,
810                                      SilcClientConnection conn,
811                                      SilcClientEntry client_entry,
812                                      const char *new_nick,
813                                      SilcClientID *new_id,
814                                      const unsigned char *idp,
815                                      SilcUInt32 idp_len)
816 {
817   char *tmp;
818
819   SILC_LOG_DEBUG(("Change nickname %s to %s", client_entry->nickname,
820                   new_nick));
821
822   /* Normalize nickname */
823   tmp = silc_identifier_check(new_nick, strlen(new_nick),
824                               SILC_STRING_UTF8, 128, NULL);
825   if (!tmp)
826     return FALSE;
827
828   /* Update the client entry */
829   silc_mutex_lock(conn->internal->lock);
830   if (!silc_idcache_update_by_context(conn->internal->client_cache,
831                                       client_entry, new_id, tmp, TRUE)) {
832     silc_free(tmp);
833     silc_mutex_unlock(conn->internal->lock);
834     return FALSE;
835   }
836   silc_mutex_unlock(conn->internal->lock);
837
838   memset(client_entry->nickname, 0, sizeof(client_entry->nickname));
839   memcpy(client_entry->nickname, new_nick, strlen(new_nick));
840   client_entry->nickname_normalized = tmp;
841   silc_client_nickname_format(client, conn, client_entry,
842                               client_entry == conn->local_entry);
843
844   /* For my client entry, update ID and set new ID to packet stream */
845   if (client_entry == conn->local_entry) {
846     if (idp && idp_len) {
847       silc_buffer_enlarge(conn->internal->local_idp, idp_len);
848       silc_buffer_put(conn->internal->local_idp, idp, idp_len);
849     }
850     if (new_id)
851       silc_packet_set_ids(conn->stream, SILC_ID_CLIENT, conn->local_id,
852                           0, NULL);
853   }
854
855   return TRUE;
856 }
857
858 /* Deletes the client entry and frees all memory. */
859
860 void silc_client_del_client_entry(SilcClient client,
861                                   SilcClientConnection conn,
862                                   SilcClientEntry client_entry)
863 {
864   silc_free(client_entry->realname);
865   silc_free(client_entry->nickname_normalized);
866   silc_free(client_entry->internal.key);
867   if (client_entry->public_key)
868     silc_pkcs_public_key_free(client_entry->public_key);
869   silc_hash_table_free(client_entry->channels);
870   if (client_entry->internal.send_key)
871     silc_cipher_free(client_entry->internal.send_key);
872   if (client_entry->internal.receive_key)
873     silc_cipher_free(client_entry->internal.receive_key);
874   if (client_entry->internal.hmac_send)
875     silc_hmac_free(client_entry->internal.hmac_send);
876   if (client_entry->internal.hmac_receive)
877     silc_hmac_free(client_entry->internal.hmac_receive);
878 #if 0
879   silc_client_ftp_session_free_client(conn, client_entry);
880   if (client_entry->internal->ke)
881     silc_client_abort_key_agreement(client, conn, client_entry);
882 #endif /* 0 */
883   silc_atomic_uninit8(&client_entry->internal.refcnt);
884   silc_free(client_entry);
885 }
886
887 /* Removes client from the cache by the client entry. */
888
889 SilcBool silc_client_del_client(SilcClient client, SilcClientConnection conn,
890                                 SilcClientEntry client_entry)
891 {
892   SilcBool ret;
893
894   if (!client_entry)
895     return FALSE;
896
897   if (silc_atomic_sub_int8(&client_entry->internal.refcnt, 1) > 0)
898     return FALSE;
899
900   SILC_LOG_DEBUG(("Deleting client %p", client_entry));
901
902   silc_mutex_lock(conn->internal->lock);
903   ret = silc_idcache_del_by_context(conn->internal->client_cache,
904                                     client_entry, NULL);
905   silc_mutex_unlock(conn->internal->lock);
906
907   if (ret) {
908     /* Remove from channels */
909     silc_client_remove_from_channels(client, conn, client_entry);
910
911     /* Free the client entry data */
912     silc_client_del_client_entry(client, conn, client_entry);
913   }
914
915   return ret;
916 }
917
918 /* Take reference of client entry */
919
920 void silc_client_ref_client(SilcClient client, SilcClientConnection conn,
921                             SilcClientEntry client_entry)
922 {
923   silc_atomic_add_int8(&client_entry->internal.refcnt, 1);
924   SILC_LOG_DEBUG(("Client %p refcnt %d->%d", client_entry,
925                   silc_atomic_get_int8(&client_entry->internal.refcnt) - 1,
926                   silc_atomic_get_int8(&client_entry->internal.refcnt)));
927 }
928
929 /* Release reference of client entry */
930
931 void silc_client_unref_client(SilcClient client, SilcClientConnection conn,
932                               SilcClientEntry client_entry)
933 {
934   if (client_entry) {
935     SILC_LOG_DEBUG(("Client %p refcnt %d->%d", client_entry,
936                     silc_atomic_get_int8(&client_entry->internal.refcnt),
937                     silc_atomic_get_int8(&client_entry->internal.refcnt) - 1));
938     silc_client_del_client(client, conn, client_entry);
939   }
940 }
941
942 /* Free client entry list */
943
944 void silc_client_list_free(SilcClient client, SilcClientConnection conn,
945                            SilcDList client_list)
946 {
947   SilcClientEntry client_entry;
948
949   if (client_list) {
950     silc_dlist_start(client_list);
951     while ((client_entry = silc_dlist_get(client_list)))
952       silc_client_unref_client(client, conn, client_entry);
953
954     silc_dlist_uninit(client_list);
955   }
956 }
957
958 /* Formats the nickname of the client specified by the `client_entry'.
959    If the format is specified by the application this will format the
960    nickname and replace the old nickname in the client entry. If the
961    format string is not specified then this function has no effect.
962    Returns the client entry that was formatted. */
963
964 SilcClientEntry silc_client_nickname_format(SilcClient client,
965                                             SilcClientConnection conn,
966                                             SilcClientEntry client_entry,
967                                             SilcBool priority)
968 {
969   char *cp;
970   char newnick[128 + 1];
971   int i, off = 0, len;
972   SilcBool freebase;
973   SilcDList clients;
974   SilcClientEntry entry, unformatted = NULL;
975
976   SILC_LOG_DEBUG(("Format nickname"));
977
978   if (!client->internal->params->nickname_format[0])
979     return client_entry;
980   if (!client_entry->nickname[0])
981     return NULL;
982
983   /* Get all clients with same nickname. Do not perform the formatting
984      if there aren't any clients with same nickname unless the application
985      is forcing us to do so. */
986   clients = silc_client_get_clients_local(client, conn,
987                                           client_entry->nickname, NULL);
988   if (!clients)
989     return NULL;
990   if (silc_dlist_count(clients) == 1 &&
991       !client->internal->params->nickname_force_format) {
992     silc_client_list_free(client, conn, clients);
993     return client_entry;
994   }
995
996   len = 0;
997   freebase = TRUE;
998   while ((entry = silc_dlist_get(clients))) {
999     if (entry->internal.valid && entry != client_entry)
1000       len++;
1001     if (entry->internal.valid && entry != client_entry &&
1002         silc_utf8_strcasecmp(entry->nickname, client_entry->nickname)) {
1003       freebase = FALSE;
1004       unformatted = entry;
1005       break;
1006     }
1007   }
1008   if (!len || freebase) {
1009     silc_client_list_free(client, conn, clients);
1010     return client_entry;
1011   }
1012
1013   /* If priority formatting, this client always gets unformatted nickname. */
1014   if (unformatted && priority)
1015     client_entry = unformatted;
1016
1017   memset(newnick, 0, sizeof(newnick));
1018   cp = client->internal->params->nickname_format;
1019   while (cp && *cp) {
1020     if (*cp == '%') {
1021       cp++;
1022       continue;
1023     }
1024
1025     switch(*cp) {
1026     case 'n':
1027       /* Nickname */
1028       if (!client_entry->nickname[0])
1029         break;
1030       len = strlen(client_entry->nickname);
1031       memcpy(&newnick[off], client_entry->nickname, len);
1032       off += len;
1033       break;
1034     case 'h':
1035       /* Stripped hostname */
1036       if (!client_entry->hostname[0])
1037         break;
1038       len = strcspn(client_entry->hostname, ".");
1039       i = strcspn(client_entry->hostname, "-");
1040       if (i < len)
1041         len = i;
1042       memcpy(&newnick[off], client_entry->hostname, len);
1043       off += len;
1044       break;
1045     case 'H':
1046       /* Full hostname */
1047       if (!client_entry->hostname[0])
1048         break;
1049       len = strlen(client_entry->hostname);
1050       memcpy(&newnick[off], client_entry->hostname, len);
1051       off += len;
1052       break;
1053     case 's':
1054       /* Stripped server name */
1055       if (!client_entry->server)
1056         break;
1057       len = strcspn(client_entry->server, ".");
1058       memcpy(&newnick[off], client_entry->server, len);
1059       off += len;
1060       break;
1061     case 'S':
1062       /* Full server name */
1063       if (!client_entry->server)
1064         break;
1065       len = strlen(client_entry->server);
1066       memcpy(&newnick[off], client_entry->server, len);
1067       off += len;
1068       break;
1069     case 'a':
1070       /* Ascending number */
1071       {
1072         char tmp[6];
1073         int num, max = 1;
1074
1075         if (silc_dlist_count(clients) == 1)
1076           break;
1077
1078         silc_dlist_start(clients);
1079         while ((entry = silc_dlist_get(clients))) {
1080           if (!silc_utf8_strncasecmp(entry->nickname, newnick, off))
1081             continue;
1082           if (strlen(entry->nickname) <= off)
1083             continue;
1084           num = atoi(&entry->nickname[off]);
1085           if (num > max)
1086             max = num;
1087         }
1088
1089         memset(tmp, 0, sizeof(tmp));
1090         silc_snprintf(tmp, sizeof(tmp) - 1, "%d", ++max);
1091         len = strlen(tmp);
1092         memcpy(&newnick[off], tmp, len);
1093         off += len;
1094       }
1095       break;
1096     default:
1097       /* Some other character in the string */
1098       memcpy(&newnick[off], cp, 1);
1099       off++;
1100       break;
1101     }
1102
1103     cp++;
1104   }
1105
1106   newnick[off] = 0;
1107   memcpy(client_entry->nickname, newnick, strlen(newnick));
1108   silc_client_list_free(client, conn, clients);
1109
1110   return client_entry;
1111 }
1112
1113 /* Parses nickname according to nickname format string */
1114
1115 SilcBool silc_client_nickname_parse(SilcClient client,
1116                                     SilcClientConnection conn,
1117                                     char *nickname,
1118                                     char **ret_nick)
1119 {
1120   char *cp, s = 0, e = 0, *nick;
1121   SilcBool n = FALSE;
1122   int len;
1123
1124   if (!client->internal->params->nickname_format[0])
1125     return TRUE;
1126
1127   if (!nickname || !nickname[0])
1128     return FALSE;
1129
1130   cp = client->internal->params->nickname_format;
1131   while (cp && *cp) {
1132     if (*cp == '%') {
1133       cp++;
1134       continue;
1135     }
1136
1137     switch(*cp) {
1138     case 'n':
1139       n = TRUE;
1140       break;
1141
1142     case 'h':
1143     case 'H':
1144     case 's':
1145     case 'S':
1146     case 'a':
1147       break;
1148
1149     default:
1150       /* Get separator character */
1151       if (n)
1152         e = *cp;
1153       else
1154         s = *cp;
1155       break;
1156     }
1157
1158      cp++;
1159   }
1160   if (!n)
1161     return FALSE;
1162
1163   /* Parse the nickname */
1164   nick = nickname;
1165   len = strlen(nick);
1166   if (s)
1167     if (strchr(nickname, s))
1168       nick = strchr(nickname, s) + 1;
1169   if (e)
1170     if (strchr(nick, e))
1171       len = strchr(nick, e) - nick;
1172   if (!len)
1173     return FALSE;
1174
1175   *ret_nick = silc_memdup(nick, len);
1176   if (!(*ret_nick))
1177     return FALSE;
1178
1179   SILC_LOG_DEBUG(("Parsed nickname: %s", *ret_nick));
1180
1181   return TRUE;
1182 }
1183
1184 /************************ Channel Searching Locally *************************/
1185
1186 /* Finds entry for channel by the channel name. Returns the entry or NULL
1187    if the entry was not found. It is found only if the client is joined
1188    to the channel. */
1189
1190 SilcChannelEntry silc_client_get_channel(SilcClient client,
1191                                          SilcClientConnection conn,
1192                                          char *channel)
1193 {
1194   SilcIDCacheEntry id_cache;
1195   SilcChannelEntry entry;
1196
1197   if (!client || !conn || !channel)
1198     return NULL;
1199
1200   SILC_LOG_DEBUG(("Find channel %s", channel));
1201
1202   /* Normalize name for search */
1203   channel = silc_channel_name_check(channel, strlen(channel), SILC_STRING_UTF8,
1204                                     256, NULL);
1205   if (!channel)
1206     return NULL;
1207
1208   silc_mutex_lock(conn->internal->lock);
1209
1210   if (!silc_idcache_find_by_name_one(conn->internal->channel_cache, channel,
1211                                      &id_cache)) {
1212     silc_mutex_unlock(conn->internal->lock);
1213     silc_free(channel);
1214     return NULL;
1215   }
1216
1217   SILC_LOG_DEBUG(("Found"));
1218
1219   entry = id_cache->context;
1220
1221   /* Reference */
1222   silc_client_ref_channel(client, conn, entry);
1223   silc_mutex_unlock(conn->internal->lock);
1224
1225   silc_free(channel);
1226
1227   return entry;
1228 }
1229
1230 /* Finds entry for channel by the channel ID. Returns the entry or NULL
1231    if the entry was not found. It is found only if the client is joined
1232    to the channel. */
1233
1234 SilcChannelEntry silc_client_get_channel_by_id(SilcClient client,
1235                                                SilcClientConnection conn,
1236                                                SilcChannelID *channel_id)
1237 {
1238   SilcIDCacheEntry id_cache;
1239   SilcChannelEntry entry;
1240
1241   if (!client || !conn || !channel_id)
1242     return NULL;
1243
1244   SILC_LOG_DEBUG(("Find channel by id %s",
1245                   silc_id_render(channel_id, SILC_ID_CHANNEL)));
1246
1247   silc_mutex_lock(conn->internal->lock);
1248
1249   if (!silc_idcache_find_by_id_one(conn->internal->channel_cache, channel_id,
1250                                    &id_cache)) {
1251     silc_mutex_unlock(conn->internal->lock);
1252     return NULL;
1253   }
1254
1255   SILC_LOG_DEBUG(("Found"));
1256
1257   entry = id_cache->context;
1258
1259   /* Reference */
1260   silc_client_ref_channel(client, conn, entry);
1261   silc_mutex_unlock(conn->internal->lock);
1262
1263   return entry;
1264 }
1265
1266 /********************** Channel Resolving from Server ***********************/
1267
1268 /* Channel resolving context */
1269 typedef struct {
1270   SilcDList channels;
1271   SilcGetChannelCallback completion;
1272   void *context;
1273 } *SilcClientGetChannelInternal;
1274
1275 /* Resolving command callback */
1276
1277 static SilcBool silc_client_get_channel_cb(SilcClient client,
1278                                            SilcClientConnection conn,
1279                                            SilcCommand command,
1280                                            SilcStatus status,
1281                                            SilcStatus error,
1282                                            void *context,
1283                                            va_list ap)
1284 {
1285   SilcClientGetChannelInternal i = context;
1286   SilcChannelEntry entry;
1287
1288   if (error != SILC_STATUS_OK) {
1289     SILC_LOG_DEBUG(("Resolving failed: %s", silc_get_status_message(error)));
1290     if (i->completion)
1291       i->completion(client, conn, error, NULL, i->context);
1292     goto out;
1293   }
1294
1295   /* Add the returned channel to list */
1296   if (i->completion) {
1297     entry = va_arg(ap, SilcChannelEntry);
1298     silc_client_ref_channel(client, conn, entry);
1299     silc_dlist_add(i->channels, entry);
1300   }
1301
1302   if (status == SILC_STATUS_OK || status == SILC_STATUS_LIST_END) {
1303     /* Deliver the channels to the caller */
1304     if (i->completion) {
1305       SILC_LOG_DEBUG(("Resolved %d channels", silc_dlist_count(i->channels)));
1306       silc_dlist_start(i->channels);
1307       i->completion(client, conn, SILC_STATUS_OK, i->channels, i->context);
1308     }
1309     goto out;
1310   }
1311
1312   return TRUE;
1313
1314  out:
1315   silc_client_list_free_channels(client, conn, i->channels);
1316   silc_free(i);
1317   return FALSE;
1318 }
1319
1320 /* Resolves channel entry from the server by the channel name. */
1321
1322 void silc_client_get_channel_resolve(SilcClient client,
1323                                      SilcClientConnection conn,
1324                                      char *channel_name,
1325                                      SilcGetChannelCallback completion,
1326                                      void *context)
1327 {
1328   SilcClientGetChannelInternal i;
1329
1330   if (!client || !conn || !channel_name || !completion)
1331     return;
1332
1333   SILC_LOG_DEBUG(("Resolve channel %s", channel_name));
1334
1335   i = silc_calloc(1, sizeof(*i));
1336   if (!i)
1337     return;
1338   i->completion = completion;
1339   i->context = context;
1340   i->channels = silc_dlist_init();
1341   if (!i->channels) {
1342     silc_free(i);
1343     return;
1344   }
1345
1346   /* Send the command */
1347   if (!silc_client_command_send(client, conn, SILC_COMMAND_IDENTIFY,
1348                                 silc_client_get_channel_cb, i, 1,
1349                                 3, channel_name, strlen(channel_name))) {
1350     if (completion)
1351       completion(client, conn, SILC_STATUS_ERR_RESOURCE_LIMIT, NULL, context);
1352   }
1353 }
1354
1355 /* Resolves channel information from the server by the channel ID. */
1356
1357 SilcUInt16
1358 silc_client_get_channel_by_id_resolve(SilcClient client,
1359                                       SilcClientConnection conn,
1360                                       SilcChannelID *channel_id,
1361                                       SilcGetChannelCallback completion,
1362                                       void *context)
1363 {
1364   SilcClientGetChannelInternal i;
1365   SilcBuffer idp;
1366   SilcUInt16 cmd_ident;
1367
1368   if (!client || !conn || !channel_id || !completion)
1369     return 0;
1370
1371   SILC_LOG_DEBUG(("Resolve channel by id %s",
1372                   silc_id_render(channel_id, SILC_ID_CHANNEL)));
1373
1374   i = silc_calloc(1, sizeof(*i));
1375   if (!i)
1376     return 0;
1377   i->completion = completion;
1378   i->context = context;
1379   i->channels = silc_dlist_init();
1380   if (!i->channels) {
1381     silc_free(i);
1382     return 0;
1383   }
1384
1385   /* Send the command */
1386   idp = silc_id_payload_encode(channel_id, SILC_ID_CHANNEL);
1387   cmd_ident = silc_client_command_send(client, conn, SILC_COMMAND_IDENTIFY,
1388                                        silc_client_get_channel_cb, i, 1,
1389                                        5, silc_buffer_datalen(idp));
1390   silc_buffer_free(idp);
1391   if (!cmd_ident && completion)
1392     completion(client, conn, SILC_STATUS_ERR_RESOURCE_LIMIT, NULL, context);
1393
1394   return cmd_ident;
1395 }
1396
1397 /************************* Channel Entry Routines ***************************/
1398
1399 /* Add new channel entry to the ID Cache */
1400
1401 SilcChannelEntry silc_client_add_channel(SilcClient client,
1402                                          SilcClientConnection conn,
1403                                          const char *channel_name,
1404                                          SilcUInt32 mode,
1405                                          SilcChannelID *channel_id)
1406 {
1407   SilcChannelEntry channel;
1408   char *channel_namec;
1409
1410   SILC_LOG_DEBUG(("Start"));
1411
1412   channel = silc_calloc(1, sizeof(*channel));
1413   if (!channel)
1414     return NULL;
1415
1416   silc_atomic_init16(&channel->internal.refcnt, 0);
1417   channel->id = *channel_id;
1418   channel->mode = mode;
1419
1420   channel->channel_name = strdup(channel_name);
1421   if (!channel->channel_name) {
1422     silc_free(channel);
1423     return NULL;
1424   }
1425
1426   channel->user_list = silc_hash_table_alloc(1, silc_hash_ptr, NULL, NULL,
1427                                              NULL, NULL, NULL, TRUE);
1428   if (!channel->user_list) {
1429     silc_free(channel->channel_name);
1430     silc_free(channel);
1431     return NULL;
1432   }
1433
1434   /* Normalize channel name */
1435   channel_namec = silc_channel_name_check(channel_name, strlen(channel_name),
1436                                           SILC_STRING_UTF8, 256, NULL);
1437   if (!channel_namec) {
1438     silc_free(channel->channel_name);
1439     silc_hash_table_free(channel->user_list);
1440     silc_free(channel);
1441     return NULL;
1442   }
1443
1444   silc_mutex_lock(conn->internal->lock);
1445
1446   /* Add channel to cache, the normalized channel name is saved to cache */
1447   if (!silc_idcache_add(conn->internal->channel_cache, channel_namec,
1448                         &channel->id, channel)) {
1449     silc_free(channel_namec);
1450     silc_free(channel->channel_name);
1451     silc_hash_table_free(channel->user_list);
1452     silc_free(channel);
1453     silc_mutex_unlock(conn->internal->lock);
1454     return NULL;
1455   }
1456
1457   silc_mutex_unlock(conn->internal->lock);
1458   silc_client_ref_channel(client, conn, channel);
1459
1460   SILC_LOG_DEBUG(("Added %p", channel));
1461
1462   return channel;
1463 }
1464
1465 /* Removes channel from the cache by the channel entry. */
1466
1467 SilcBool silc_client_del_channel(SilcClient client, SilcClientConnection conn,
1468                                  SilcChannelEntry channel)
1469 {
1470   SilcBool ret;
1471   SilcCipher key;
1472   SilcHmac hmac;
1473
1474   if (!channel)
1475     return FALSE;
1476
1477   if (silc_atomic_sub_int16(&channel->internal.refcnt, 1) > 0)
1478     return FALSE;
1479
1480   SILC_LOG_DEBUG(("Deleting channel %p", channel));
1481
1482   silc_mutex_lock(conn->internal->lock);
1483   ret = silc_idcache_del_by_context(conn->internal->channel_cache,
1484                                     channel, NULL);
1485   silc_mutex_unlock(conn->internal->lock);
1486
1487   if (!ret)
1488     return FALSE;
1489
1490   silc_client_empty_channel(client, conn, channel);
1491   silc_hash_table_free(channel->user_list);
1492   silc_free(channel->channel_name);
1493   silc_free(channel->topic);
1494   if (channel->founder_key)
1495     silc_pkcs_public_key_free(channel->founder_key);
1496   if (channel->internal.channel_key)
1497     silc_cipher_free(channel->internal.channel_key);
1498   if (channel->internal.hmac)
1499     silc_hmac_free(channel->internal.hmac);
1500   if (channel->internal.old_channel_keys) {
1501     silc_dlist_start(channel->internal.old_channel_keys);
1502     while ((key = silc_dlist_get(channel->internal.old_channel_keys)))
1503       silc_cipher_free(key);
1504     silc_dlist_uninit(channel->internal.old_channel_keys);
1505   }
1506   if (channel->internal.old_hmacs) {
1507     silc_dlist_start(channel->internal.old_hmacs);
1508     while ((hmac = silc_dlist_get(channel->internal.old_hmacs)))
1509       silc_hmac_free(hmac);
1510     silc_dlist_uninit(channel->internal.old_hmacs);
1511   }
1512   if (channel->channel_pubkeys)
1513     silc_argument_list_free(channel->channel_pubkeys,
1514                             SILC_ARGUMENT_PUBLIC_KEY);
1515   silc_client_del_channel_private_keys(client, conn, channel);
1516   silc_atomic_uninit16(&channel->internal.refcnt);
1517   silc_schedule_task_del_by_context(conn->client->schedule, channel);
1518   silc_free(channel);
1519
1520   return ret;
1521 }
1522
1523 /* Replaces the channel ID of the `channel' to `new_id'. Returns FALSE
1524    if the ID could not be changed. */
1525
1526 SilcBool silc_client_replace_channel_id(SilcClient client,
1527                                         SilcClientConnection conn,
1528                                         SilcChannelEntry channel,
1529                                         SilcChannelID *new_id)
1530 {
1531   SilcBool ret = FALSE;
1532
1533   if (!new_id)
1534     return FALSE;
1535
1536   SILC_LOG_DEBUG(("Old Channel ID id(%s)",
1537                   silc_id_render(&channel->id, SILC_ID_CHANNEL)));
1538   SILC_LOG_DEBUG(("New Channel ID id(%s)",
1539                   silc_id_render(new_id, SILC_ID_CHANNEL)));
1540
1541   /* Update the ID */
1542   silc_mutex_lock(conn->internal->lock);
1543   silc_idcache_update_by_context(conn->internal->channel_cache, channel,
1544                                  new_id, NULL, FALSE);
1545   silc_mutex_unlock(conn->internal->lock);
1546
1547   return ret;
1548 }
1549
1550 /* Take reference of channel entry */
1551
1552 void silc_client_ref_channel(SilcClient client, SilcClientConnection conn,
1553                              SilcChannelEntry channel_entry)
1554 {
1555   silc_atomic_add_int16(&channel_entry->internal.refcnt, 1);
1556   SILC_LOG_DEBUG(("Channel %p refcnt %d->%d", channel_entry,
1557                   silc_atomic_get_int16(&channel_entry->internal.refcnt) - 1,
1558                   silc_atomic_get_int16(&channel_entry->internal.refcnt)));
1559 }
1560
1561 /* Release reference of channel entry */
1562
1563 void silc_client_unref_channel(SilcClient client, SilcClientConnection conn,
1564                                SilcChannelEntry channel_entry)
1565 {
1566   if (channel_entry) {
1567     SILC_LOG_DEBUG(("Channel %p refcnt %d->%d", channel_entry,
1568                     silc_atomic_get_int16(&channel_entry->internal.refcnt),
1569                     silc_atomic_get_int16(&channel_entry->internal.refcnt)
1570                     - 1));
1571     silc_client_del_channel(client, conn, channel_entry);
1572   }
1573 }
1574
1575 /* Free channel entry list */
1576
1577 void silc_client_list_free_channels(SilcClient client,
1578                                     SilcClientConnection conn,
1579                                     SilcDList channel_list)
1580 {
1581   SilcChannelEntry channel_entry;
1582
1583   if (channel_list) {
1584     silc_dlist_start(channel_list);
1585     while ((channel_entry = silc_dlist_get(channel_list)))
1586       silc_client_unref_channel(client, conn, channel_entry);
1587
1588     silc_dlist_uninit(channel_list);
1589   }
1590 }
1591
1592 /************************* Server Searching Locally *************************/
1593
1594 /* Finds entry for server by the server name. */
1595
1596 SilcServerEntry silc_client_get_server(SilcClient client,
1597                                        SilcClientConnection conn,
1598                                        char *server_name)
1599 {
1600   SilcIDCacheEntry id_cache;
1601   SilcServerEntry entry;
1602
1603   if (!client || !conn || !server_name)
1604     return NULL;
1605
1606   SILC_LOG_DEBUG(("Find server by name %s", server_name));
1607
1608   /* Normalize server name for search */
1609   server_name = silc_identifier_check(server_name, strlen(server_name),
1610                                       SILC_STRING_UTF8, 256, NULL);
1611   if (!server_name)
1612     return NULL;
1613
1614   silc_mutex_lock(conn->internal->lock);
1615
1616   if (!silc_idcache_find_by_name_one(conn->internal->server_cache,
1617                                      server_name, &id_cache)) {
1618     silc_free(server_name);
1619     silc_mutex_unlock(conn->internal->lock);
1620     return NULL;
1621   }
1622
1623   SILC_LOG_DEBUG(("Found"));
1624
1625   /* Reference */
1626   entry = id_cache->context;
1627   silc_client_ref_server(client, conn, entry);
1628
1629   silc_mutex_unlock(conn->internal->lock);
1630
1631   silc_free(server_name);
1632
1633   return entry;
1634 }
1635
1636 /* Finds entry for server by the server ID. */
1637
1638 SilcServerEntry silc_client_get_server_by_id(SilcClient client,
1639                                              SilcClientConnection conn,
1640                                              SilcServerID *server_id)
1641 {
1642   SilcIDCacheEntry id_cache;
1643   SilcServerEntry entry;
1644
1645   if (!client || !conn || !server_id)
1646     return NULL;
1647
1648   SILC_LOG_DEBUG(("Find server by id %s",
1649                   silc_id_render(server_id, SILC_ID_SERVER)));
1650
1651   silc_mutex_lock(conn->internal->lock);
1652
1653   if (!silc_idcache_find_by_id_one(conn->internal->server_cache,
1654                                    server_id, &id_cache)) {
1655     silc_mutex_unlock(conn->internal->lock);
1656     return NULL;
1657   }
1658
1659   SILC_LOG_DEBUG(("Found"));
1660
1661   /* Reference */
1662   entry = id_cache->context;
1663   silc_client_ref_server(client, conn, entry);
1664
1665   silc_mutex_unlock(conn->internal->lock);
1666
1667   return entry;
1668 }
1669
1670 /*********************** Server Resolving from Server ***********************/
1671
1672 /* Resolving context */
1673 typedef struct {
1674   SilcDList servers;
1675   SilcGetServerCallback completion;
1676   void *context;
1677 } *SilcClientGetServerInternal;
1678
1679 /* Resolving command callback */
1680
1681 static SilcBool silc_client_get_server_cb(SilcClient client,
1682                                           SilcClientConnection conn,
1683                                           SilcCommand command,
1684                                           SilcStatus status,
1685                                           SilcStatus error,
1686                                           void *context,
1687                                           va_list ap)
1688 {
1689   SilcClientGetServerInternal i = context;
1690   SilcServerEntry server;
1691
1692   if (error != SILC_STATUS_OK) {
1693     SILC_LOG_DEBUG(("Resolving failed: %s", silc_get_status_message(error)));
1694     if (i->completion)
1695       i->completion(client, conn, error, NULL, i->context);
1696     goto out;
1697   }
1698
1699   /* Add the returned servers to list */
1700   if (i->completion) {
1701     server = va_arg(ap, SilcServerEntry);
1702     silc_client_ref_server(client, conn, server);
1703     silc_dlist_add(i->servers, server);
1704     server->internal.resolve_cmd_ident = 0;
1705   }
1706
1707   if (status == SILC_STATUS_OK || status == SILC_STATUS_LIST_END) {
1708     /* Deliver the servers to the caller */
1709     if (i->completion) {
1710       SILC_LOG_DEBUG(("Resolved %d servers", silc_dlist_count(i->servers)));
1711       silc_dlist_start(i->servers);
1712       i->completion(client, conn, SILC_STATUS_OK, i->servers, i->context);
1713     }
1714     goto out;
1715   }
1716
1717   return TRUE;
1718
1719  out:
1720   silc_client_list_free_servers(client, conn, i->servers);
1721   silc_free(i);
1722   return FALSE;
1723 }
1724
1725 /* Resolve server by server ID */
1726
1727 SilcUInt16
1728 silc_client_get_server_by_id_resolve(SilcClient client,
1729                                      SilcClientConnection conn,
1730                                      SilcServerID *server_id,
1731                                      SilcGetServerCallback completion,
1732                                      void *context)
1733 {
1734   SilcClientGetServerInternal i;
1735   SilcServerEntry server;
1736   SilcBuffer idp;
1737   SilcUInt16 cmd_ident;
1738
1739   if (!client || !conn || !server_id || !completion)
1740     return 0;
1741
1742   SILC_LOG_DEBUG(("Resolve server by id %s",
1743                   silc_id_render(server_id, SILC_ID_SERVER)));
1744
1745   i = silc_calloc(1, sizeof(*i));
1746   if (!i)
1747     return 0;
1748   i->completion = completion;
1749   i->context = context;
1750   i->servers = silc_dlist_init();
1751   if (!i->servers) {
1752     silc_free(i);
1753     return 0;
1754   }
1755
1756   /* Attach to resolving, if on going */
1757   server = silc_client_get_server_by_id(client, conn, server_id);
1758   if (server && server->internal.resolve_cmd_ident) {
1759     SILC_LOG_DEBUG(("Attach to existing resolving"));
1760     silc_client_unref_server(client, conn, server);
1761     silc_client_command_pending(conn, SILC_COMMAND_NONE,
1762                                 server->internal.resolve_cmd_ident,
1763                                 silc_client_get_server_cb, i);
1764     return server->internal.resolve_cmd_ident;
1765   }
1766
1767   /* Send the command */
1768   idp = silc_id_payload_encode(server_id, SILC_ID_SERVER);
1769   cmd_ident = silc_client_command_send(client, conn, SILC_COMMAND_IDENTIFY,
1770                                        silc_client_get_server_cb, i, 1,
1771                                        5, silc_buffer_datalen(idp));
1772   silc_buffer_free(idp);
1773   if (!cmd_ident && completion)
1774     completion(client, conn, SILC_STATUS_ERR_RESOURCE_LIMIT, NULL, context);
1775
1776   if (server && cmd_ident)
1777     server->internal.resolve_cmd_ident = cmd_ident;
1778
1779   silc_client_unref_server(client, conn, server);
1780
1781   return cmd_ident;
1782 }
1783
1784 /************************** Server Entry Routines ***************************/
1785
1786 /* Add new server entry */
1787
1788 SilcServerEntry silc_client_add_server(SilcClient client,
1789                                        SilcClientConnection conn,
1790                                        const char *server_name,
1791                                        const char *server_info,
1792                                        SilcServerID *server_id)
1793 {
1794   SilcServerEntry server_entry;
1795   char *server_namec = NULL;
1796
1797   if (!server_id)
1798     return NULL;
1799
1800   SILC_LOG_DEBUG(("Adding new server %s", server_name));
1801
1802   server_entry = silc_calloc(1, sizeof(*server_entry));
1803   if (!server_entry)
1804     return NULL;
1805
1806   silc_atomic_init8(&server_entry->internal.refcnt, 0);
1807   server_entry->id = *server_id;
1808   if (server_name)
1809     server_entry->server_name = strdup(server_name);
1810   if (server_info)
1811     server_entry->server_info = strdup(server_info);
1812
1813   /* Normalize server name */
1814   if (server_name) {
1815     server_namec = silc_identifier_check(server_name, strlen(server_name),
1816                                          SILC_STRING_UTF8, 256, NULL);
1817     if (!server_namec) {
1818       silc_free(server_entry->server_name);
1819       silc_free(server_entry->server_info);
1820       silc_free(server_entry);
1821       return NULL;
1822     }
1823   }
1824
1825   silc_mutex_lock(conn->internal->lock);
1826
1827   /* Add server to cache */
1828   if (!silc_idcache_add(conn->internal->server_cache, server_namec,
1829                         &server_entry->id, server_entry)) {
1830     silc_free(server_namec);
1831     silc_free(server_entry->server_name);
1832     silc_free(server_entry->server_info);
1833     silc_free(server_entry);
1834     silc_mutex_unlock(conn->internal->lock);
1835     return NULL;
1836   }
1837
1838   silc_mutex_unlock(conn->internal->lock);
1839   silc_client_ref_server(client, conn, server_entry);
1840
1841   SILC_LOG_DEBUG(("Added %p", server_entry));
1842
1843   return server_entry;
1844 }
1845
1846 /* Removes server from the cache by the server entry. */
1847
1848 SilcBool silc_client_del_server(SilcClient client, SilcClientConnection conn,
1849                                 SilcServerEntry server)
1850 {
1851   SilcBool ret;
1852
1853   if (!server)
1854     return FALSE;
1855
1856   if (silc_atomic_sub_int8(&server->internal.refcnt, 1) > 0)
1857     return FALSE;
1858
1859   SILC_LOG_DEBUG(("Deleting server %p", server));
1860
1861   silc_mutex_lock(conn->internal->lock);
1862   ret = silc_idcache_del_by_context(conn->internal->server_cache,
1863                                     server, NULL);
1864   silc_mutex_unlock(conn->internal->lock);
1865
1866   silc_free(server->server_name);
1867   silc_free(server->server_info);
1868   if (server->public_key)
1869     silc_pkcs_public_key_free(server->public_key);
1870   silc_atomic_uninit8(&server->internal.refcnt);
1871   silc_free(server);
1872
1873   return ret;
1874 }
1875
1876 /* Updates the `server_entry' with the new information sent as argument. */
1877
1878 void silc_client_update_server(SilcClient client,
1879                                SilcClientConnection conn,
1880                                SilcServerEntry server_entry,
1881                                const char *server_name,
1882                                const char *server_info)
1883 {
1884   char *server_namec = NULL;
1885
1886   SILC_LOG_DEBUG(("Updating server %p", server_entry));
1887
1888   if (server_name &&
1889       (!server_entry->server_name ||
1890        !silc_utf8_strcasecmp(server_entry->server_name, server_name))) {
1891
1892     server_namec = silc_identifier_check(server_name, strlen(server_name),
1893                                          SILC_STRING_UTF8, 256, NULL);
1894     if (!server_namec)
1895       return;
1896
1897     silc_free(server_entry->server_name);
1898     server_entry->server_name = strdup(server_name);
1899     if (!server_entry->server_name)
1900       return;
1901
1902     /* Update cache entry */
1903     silc_mutex_lock(conn->internal->lock);
1904     silc_idcache_update_by_context(conn->internal->server_cache, server_entry,
1905                                    NULL, server_namec, TRUE);
1906     silc_mutex_unlock(conn->internal->lock);
1907   }
1908
1909   if (server_info &&
1910       (!server_entry->server_info ||
1911        !silc_utf8_strcasecmp(server_entry->server_info, server_info))) {
1912     silc_free(server_entry->server_info);
1913     server_entry->server_info = strdup(server_info);
1914   }
1915 }
1916
1917 /* Take reference of server entry */
1918
1919 void silc_client_ref_server(SilcClient client, SilcClientConnection conn,
1920                             SilcServerEntry server_entry)
1921 {
1922   silc_atomic_add_int8(&server_entry->internal.refcnt, 1);
1923   SILC_LOG_DEBUG(("Server %p refcnt %d->%d", server_entry,
1924                   silc_atomic_get_int8(&server_entry->internal.refcnt) - 1,
1925                   silc_atomic_get_int8(&server_entry->internal.refcnt)));
1926 }
1927
1928 /* Release reference of server entry */
1929
1930 void silc_client_unref_server(SilcClient client, SilcClientConnection conn,
1931                               SilcServerEntry server_entry)
1932 {
1933   if (server_entry) {
1934     SILC_LOG_DEBUG(("Server %p refcnt %d->%d", server_entry,
1935                     silc_atomic_get_int8(&server_entry->internal.refcnt),
1936                     silc_atomic_get_int8(&server_entry->internal.refcnt)
1937                     - 1));
1938     silc_client_del_server(client, conn, server_entry);
1939   }
1940 }
1941
1942 /* Free server entry list */
1943
1944 void silc_client_list_free_servers(SilcClient client,
1945                                    SilcClientConnection conn,
1946                                    SilcDList server_list)
1947 {
1948   SilcServerEntry server_entry;
1949
1950   if (server_list) {
1951     silc_dlist_start(server_list);
1952     while ((server_entry = silc_dlist_get(server_list)))
1953       silc_client_unref_server(client, conn, server_entry);
1954
1955     silc_dlist_uninit(server_list);
1956   }
1957 }