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