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