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