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