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