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