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