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