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