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