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