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