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