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