Sun Mar 11 15:22:42 CET 2007 Jochen Eisinger <coffee@silcnet.org>
[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   silc_client_ftp_session_free_client(client, client_entry);
907   if (client_entry->internal.ke)
908     silc_client_abort_key_agreement(client, conn, client_entry);
909   silc_atomic_uninit8(&client_entry->internal.refcnt);
910   silc_rwlock_free(client_entry->internal.lock);
911   silc_free(client_entry);
912 }
913
914 /* Removes client from the cache by the client entry. */
915
916 SilcBool silc_client_del_client(SilcClient client, SilcClientConnection conn,
917                                 SilcClientEntry client_entry)
918 {
919   SilcBool ret;
920
921   if (!client_entry)
922     return FALSE;
923
924   if (silc_atomic_sub_int8(&client_entry->internal.refcnt, 1) > 0)
925     return FALSE;
926
927   SILC_LOG_DEBUG(("Deleting client %p", client_entry));
928
929   silc_mutex_lock(conn->internal->lock);
930   ret = silc_idcache_del_by_context(conn->internal->client_cache,
931                                     client_entry, NULL);
932   silc_mutex_unlock(conn->internal->lock);
933
934   if (ret) {
935     /* Remove from channels */
936     silc_client_remove_from_channels(client, conn, client_entry);
937
938     /* Free the client entry data */
939     silc_client_del_client_entry(client, conn, client_entry);
940   }
941
942   return ret;
943 }
944
945 /* Internal routine used to find client by ID and if not found this creates
946    new client entry and returns it. */
947
948 SilcClientEntry silc_client_get_client(SilcClient client,
949                                        SilcClientConnection conn,
950                                        SilcClientID *client_id)
951 {
952   SilcClientEntry client_entry;
953
954   client_entry = silc_client_get_client_by_id(client, conn, client_id);
955   if (!client_entry) {
956     client_entry = silc_client_add_client(client, conn, NULL, NULL, NULL,
957                                           client_id, 0);
958     if (!client_entry)
959       return NULL;
960     silc_client_ref_client(client, conn, client_entry);
961   }
962
963   return client_entry;
964 }
965
966 /* Lock client */
967
968 void silc_client_lock_client(SilcClientEntry client_entry)
969 {
970   silc_rwlock_rdlock(client_entry->internal.lock);
971 }
972
973 /* Unlock client */
974
975 void silc_client_unlock_client(SilcClientEntry client_entry)
976 {
977   silc_rwlock_unlock(client_entry->internal.lock);
978 }
979
980 /* Take reference of client entry */
981
982 SilcClientEntry silc_client_ref_client(SilcClient client,
983                                        SilcClientConnection conn,
984                                        SilcClientEntry client_entry)
985 {
986   silc_atomic_add_int8(&client_entry->internal.refcnt, 1);
987   SILC_LOG_DEBUG(("Client %p refcnt %d->%d", client_entry,
988                   silc_atomic_get_int8(&client_entry->internal.refcnt) - 1,
989                   silc_atomic_get_int8(&client_entry->internal.refcnt)));
990   return client_entry;
991 }
992
993 /* Release reference of client entry */
994
995 void silc_client_unref_client(SilcClient client, SilcClientConnection conn,
996                               SilcClientEntry client_entry)
997 {
998   if (client_entry) {
999     SILC_LOG_DEBUG(("Client %p refcnt %d->%d", client_entry,
1000                     silc_atomic_get_int8(&client_entry->internal.refcnt),
1001                     silc_atomic_get_int8(&client_entry->internal.refcnt) - 1));
1002     silc_client_del_client(client, conn, client_entry);
1003   }
1004 }
1005
1006 /* Free client entry list */
1007
1008 void silc_client_list_free(SilcClient client, SilcClientConnection conn,
1009                            SilcDList client_list)
1010 {
1011   SilcClientEntry client_entry;
1012
1013   if (client_list) {
1014     silc_dlist_start(client_list);
1015     while ((client_entry = silc_dlist_get(client_list)))
1016       silc_client_unref_client(client, conn, client_entry);
1017
1018     silc_dlist_uninit(client_list);
1019   }
1020 }
1021
1022 /* Formats the nickname of the client specified by the `client_entry'.
1023    If the format is specified by the application this will format the
1024    nickname and replace the old nickname in the client entry. If the
1025    format string is not specified then this function has no effect.
1026    Returns the client entry that was formatted. */
1027
1028 SilcClientEntry silc_client_nickname_format(SilcClient client,
1029                                             SilcClientConnection conn,
1030                                             SilcClientEntry client_entry,
1031                                             SilcBool priority)
1032 {
1033   char *cp;
1034   char newnick[128 + 1];
1035   int i, off = 0, len;
1036   SilcBool freebase;
1037   SilcDList clients;
1038   SilcClientEntry entry, unformatted = NULL;
1039
1040   if (!client->internal->params->nickname_format[0])
1041     return client_entry;
1042   if (!client_entry->nickname[0])
1043     return NULL;
1044
1045   SILC_LOG_DEBUG(("Format nickname"));
1046
1047   /* Get all clients with same nickname. Do not perform the formatting
1048      if there aren't any clients with same nickname unless the application
1049      is forcing us to do so. */
1050   clients = silc_client_get_clients_local(client, conn,
1051                                           client_entry->nickname, NULL);
1052   if (!clients)
1053     return NULL;
1054   if (silc_dlist_count(clients) == 1 &&
1055       !client->internal->params->nickname_force_format) {
1056     silc_client_list_free(client, conn, clients);
1057     return client_entry;
1058   }
1059
1060   len = 0;
1061   freebase = TRUE;
1062   while ((entry = silc_dlist_get(clients))) {
1063     if (entry->internal.valid && entry != client_entry)
1064       len++;
1065     if (entry->internal.valid && entry != client_entry &&
1066         silc_utf8_strcasecmp(entry->nickname, client_entry->nickname)) {
1067       freebase = FALSE;
1068       unformatted = entry;
1069       break;
1070     }
1071   }
1072   if (!len || freebase) {
1073     silc_client_list_free(client, conn, clients);
1074     return client_entry;
1075   }
1076
1077   /* If priority formatting, this client always gets unformatted nickname. */
1078   if (unformatted && priority)
1079     client_entry = unformatted;
1080
1081   memset(newnick, 0, sizeof(newnick));
1082   cp = client->internal->params->nickname_format;
1083   while (cp && *cp) {
1084     if (*cp == '%') {
1085       cp++;
1086       continue;
1087     }
1088
1089     switch(*cp) {
1090     case 'n':
1091       /* Nickname */
1092       if (!client_entry->nickname[0])
1093         break;
1094       len = strlen(client_entry->nickname);
1095       memcpy(&newnick[off], client_entry->nickname, len);
1096       off += len;
1097       break;
1098     case 'h':
1099       /* Stripped hostname */
1100       if (!client_entry->hostname[0])
1101         break;
1102       len = strcspn(client_entry->hostname, ".");
1103       i = strcspn(client_entry->hostname, "-");
1104       if (i < len)
1105         len = i;
1106       memcpy(&newnick[off], client_entry->hostname, len);
1107       off += len;
1108       break;
1109     case 'H':
1110       /* Full hostname */
1111       if (!client_entry->hostname[0])
1112         break;
1113       len = strlen(client_entry->hostname);
1114       memcpy(&newnick[off], client_entry->hostname, len);
1115       off += len;
1116       break;
1117     case 's':
1118       /* Stripped server name */
1119       if (!client_entry->server)
1120         break;
1121       len = strcspn(client_entry->server, ".");
1122       memcpy(&newnick[off], client_entry->server, len);
1123       off += len;
1124       break;
1125     case 'S':
1126       /* Full server name */
1127       if (!client_entry->server)
1128         break;
1129       len = strlen(client_entry->server);
1130       memcpy(&newnick[off], client_entry->server, len);
1131       off += len;
1132       break;
1133     case 'a':
1134       /* Ascending number */
1135       {
1136         char tmp[6];
1137         int num, max = 1;
1138
1139         if (silc_dlist_count(clients) == 1)
1140           break;
1141
1142         silc_dlist_start(clients);
1143         while ((entry = silc_dlist_get(clients))) {
1144           if (!silc_utf8_strncasecmp(entry->nickname, newnick, off))
1145             continue;
1146           if (strlen(entry->nickname) <= off)
1147             continue;
1148           num = atoi(&entry->nickname[off]);
1149           if (num > max)
1150             max = num;
1151         }
1152
1153         memset(tmp, 0, sizeof(tmp));
1154         silc_snprintf(tmp, sizeof(tmp) - 1, "%d", ++max);
1155         len = strlen(tmp);
1156         memcpy(&newnick[off], tmp, len);
1157         off += len;
1158       }
1159       break;
1160     default:
1161       /* Some other character in the string */
1162       memcpy(&newnick[off], cp, 1);
1163       off++;
1164       break;
1165     }
1166
1167     cp++;
1168   }
1169
1170   newnick[off] = 0;
1171   memcpy(client_entry->nickname, newnick, strlen(newnick));
1172   silc_client_list_free(client, conn, clients);
1173
1174   return client_entry;
1175 }
1176
1177 /* Parses nickname according to nickname format string */
1178
1179 SilcBool silc_client_nickname_parse(SilcClient client,
1180                                     SilcClientConnection conn,
1181                                     char *nickname,
1182                                     char **ret_nick)
1183 {
1184   char *cp, s = 0, e = 0, *nick;
1185   SilcBool n = FALSE;
1186   int len;
1187
1188   if (!client->internal->params->nickname_format[0])
1189     return TRUE;
1190
1191   if (!nickname || !nickname[0])
1192     return FALSE;
1193
1194   cp = client->internal->params->nickname_format;
1195   while (cp && *cp) {
1196     if (*cp == '%') {
1197       cp++;
1198       continue;
1199     }
1200
1201     switch(*cp) {
1202     case 'n':
1203       n = TRUE;
1204       break;
1205
1206     case 'h':
1207     case 'H':
1208     case 's':
1209     case 'S':
1210     case 'a':
1211       break;
1212
1213     default:
1214       /* Get separator character */
1215       if (n)
1216         e = *cp;
1217       else
1218         s = *cp;
1219       break;
1220     }
1221
1222      cp++;
1223   }
1224   if (!n)
1225     return FALSE;
1226
1227   /* Parse the nickname */
1228   nick = nickname;
1229   len = strlen(nick);
1230   if (s)
1231     if (strchr(nickname, s))
1232       nick = strchr(nickname, s) + 1;
1233   if (e)
1234     if (strchr(nick, e))
1235       len = strchr(nick, e) - nick;
1236   if (!len)
1237     return FALSE;
1238
1239   *ret_nick = silc_memdup(nick, len);
1240   if (!(*ret_nick))
1241     return FALSE;
1242
1243   SILC_LOG_DEBUG(("Parsed nickname: %s", *ret_nick));
1244
1245   return TRUE;
1246 }
1247
1248 /************************ Channel Searching Locally *************************/
1249
1250 /* Finds entry for channel by the channel name. Returns the entry or NULL
1251    if the entry was not found. It is found only if the client is joined
1252    to the channel. */
1253
1254 SilcChannelEntry silc_client_get_channel(SilcClient client,
1255                                          SilcClientConnection conn,
1256                                          char *channel)
1257 {
1258   SilcIDCacheEntry id_cache;
1259   SilcChannelEntry entry;
1260
1261   if (!client || !conn || !channel)
1262     return NULL;
1263
1264   SILC_LOG_DEBUG(("Find channel %s", channel));
1265
1266   /* Normalize name for search */
1267   channel = silc_channel_name_check(channel, strlen(channel), SILC_STRING_UTF8,
1268                                     256, NULL);
1269   if (!channel)
1270     return NULL;
1271
1272   silc_mutex_lock(conn->internal->lock);
1273
1274   if (!silc_idcache_find_by_name_one(conn->internal->channel_cache, channel,
1275                                      &id_cache)) {
1276     silc_mutex_unlock(conn->internal->lock);
1277     silc_free(channel);
1278     return NULL;
1279   }
1280
1281   SILC_LOG_DEBUG(("Found"));
1282
1283   entry = id_cache->context;
1284
1285   /* Reference */
1286   silc_client_ref_channel(client, conn, entry);
1287   silc_mutex_unlock(conn->internal->lock);
1288
1289   silc_free(channel);
1290
1291   return entry;
1292 }
1293
1294 /* Finds entry for channel by the channel ID. Returns the entry or NULL
1295    if the entry was not found. It is found only if the client is joined
1296    to the channel. */
1297
1298 SilcChannelEntry silc_client_get_channel_by_id(SilcClient client,
1299                                                SilcClientConnection conn,
1300                                                SilcChannelID *channel_id)
1301 {
1302   SilcIDCacheEntry id_cache;
1303   SilcChannelEntry entry;
1304
1305   if (!client || !conn || !channel_id)
1306     return NULL;
1307
1308   SILC_LOG_DEBUG(("Find channel by id %s",
1309                   silc_id_render(channel_id, SILC_ID_CHANNEL)));
1310
1311   silc_mutex_lock(conn->internal->lock);
1312
1313   if (!silc_idcache_find_by_id_one(conn->internal->channel_cache, channel_id,
1314                                    &id_cache)) {
1315     silc_mutex_unlock(conn->internal->lock);
1316     return NULL;
1317   }
1318
1319   SILC_LOG_DEBUG(("Found"));
1320
1321   entry = id_cache->context;
1322
1323   /* Reference */
1324   silc_client_ref_channel(client, conn, entry);
1325   silc_mutex_unlock(conn->internal->lock);
1326
1327   return entry;
1328 }
1329
1330 /********************** Channel Resolving from Server ***********************/
1331
1332 /* Channel resolving context */
1333 typedef struct {
1334   SilcDList channels;
1335   SilcGetChannelCallback completion;
1336   void *context;
1337 } *SilcClientGetChannelInternal;
1338
1339 /* Resolving command callback */
1340
1341 static SilcBool silc_client_get_channel_cb(SilcClient client,
1342                                            SilcClientConnection conn,
1343                                            SilcCommand command,
1344                                            SilcStatus status,
1345                                            SilcStatus error,
1346                                            void *context,
1347                                            va_list ap)
1348 {
1349   SilcClientGetChannelInternal i = context;
1350   SilcChannelEntry entry;
1351
1352   if (error != SILC_STATUS_OK) {
1353     SILC_LOG_DEBUG(("Resolving failed: %s", silc_get_status_message(error)));
1354     if (i->completion)
1355       i->completion(client, conn, error, NULL, i->context);
1356     goto out;
1357   }
1358
1359   /* Add the returned channel to list */
1360   if (i->completion) {
1361     entry = va_arg(ap, SilcChannelEntry);
1362     silc_client_ref_channel(client, conn, entry);
1363     silc_dlist_add(i->channels, entry);
1364   }
1365
1366   if (status == SILC_STATUS_OK || status == SILC_STATUS_LIST_END) {
1367     /* Deliver the channels to the caller */
1368     if (i->completion) {
1369       SILC_LOG_DEBUG(("Resolved %d channels", silc_dlist_count(i->channels)));
1370       silc_dlist_start(i->channels);
1371       i->completion(client, conn, SILC_STATUS_OK, i->channels, i->context);
1372     }
1373     goto out;
1374   }
1375
1376   return TRUE;
1377
1378  out:
1379   silc_client_list_free_channels(client, conn, i->channels);
1380   silc_free(i);
1381   return FALSE;
1382 }
1383
1384 /* Resolves channel entry from the server by the channel name. */
1385
1386 void silc_client_get_channel_resolve(SilcClient client,
1387                                      SilcClientConnection conn,
1388                                      char *channel_name,
1389                                      SilcGetChannelCallback completion,
1390                                      void *context)
1391 {
1392   SilcClientGetChannelInternal i;
1393
1394   if (!client || !conn || !channel_name || !completion)
1395     return;
1396
1397   SILC_LOG_DEBUG(("Resolve channel %s", channel_name));
1398
1399   i = silc_calloc(1, sizeof(*i));
1400   if (!i)
1401     return;
1402   i->completion = completion;
1403   i->context = context;
1404   i->channels = silc_dlist_init();
1405   if (!i->channels) {
1406     silc_free(i);
1407     return;
1408   }
1409
1410   /* Send the command */
1411   if (!silc_client_command_send(client, conn, SILC_COMMAND_IDENTIFY,
1412                                 silc_client_get_channel_cb, i, 1,
1413                                 3, channel_name, strlen(channel_name))) {
1414     if (completion)
1415       completion(client, conn, SILC_STATUS_ERR_RESOURCE_LIMIT, NULL, context);
1416   }
1417 }
1418
1419 /* Resolves channel information from the server by the channel ID. */
1420
1421 SilcUInt16
1422 silc_client_get_channel_by_id_resolve(SilcClient client,
1423                                       SilcClientConnection conn,
1424                                       SilcChannelID *channel_id,
1425                                       SilcGetChannelCallback completion,
1426                                       void *context)
1427 {
1428   SilcClientGetChannelInternal i;
1429   SilcBuffer idp;
1430   SilcUInt16 cmd_ident;
1431
1432   if (!client || !conn || !channel_id || !completion)
1433     return 0;
1434
1435   SILC_LOG_DEBUG(("Resolve channel by id %s",
1436                   silc_id_render(channel_id, SILC_ID_CHANNEL)));
1437
1438   i = silc_calloc(1, sizeof(*i));
1439   if (!i)
1440     return 0;
1441   i->completion = completion;
1442   i->context = context;
1443   i->channels = silc_dlist_init();
1444   if (!i->channels) {
1445     silc_free(i);
1446     return 0;
1447   }
1448
1449   /* Send the command */
1450   idp = silc_id_payload_encode(channel_id, SILC_ID_CHANNEL);
1451   cmd_ident = silc_client_command_send(client, conn, SILC_COMMAND_IDENTIFY,
1452                                        silc_client_get_channel_cb, i, 1,
1453                                        5, silc_buffer_datalen(idp));
1454   silc_buffer_free(idp);
1455   if (!cmd_ident && completion)
1456     completion(client, conn, SILC_STATUS_ERR_RESOURCE_LIMIT, NULL, context);
1457
1458   return cmd_ident;
1459 }
1460
1461 /************************* Channel Entry Routines ***************************/
1462
1463 /* Add new channel entry to the ID Cache */
1464
1465 SilcChannelEntry silc_client_add_channel(SilcClient client,
1466                                          SilcClientConnection conn,
1467                                          const char *channel_name,
1468                                          SilcUInt32 mode,
1469                                          SilcChannelID *channel_id)
1470 {
1471   SilcChannelEntry channel;
1472   char *channel_namec;
1473
1474   SILC_LOG_DEBUG(("Start"));
1475
1476   channel = silc_calloc(1, sizeof(*channel));
1477   if (!channel)
1478     return NULL;
1479
1480   silc_rwlock_alloc(&channel->internal.lock);
1481   silc_atomic_init16(&channel->internal.refcnt, 0);
1482   channel->id = *channel_id;
1483   channel->mode = mode;
1484
1485   channel->channel_name = strdup(channel_name);
1486   if (!channel->channel_name) {
1487     silc_free(channel);
1488     return NULL;
1489   }
1490
1491   channel->user_list = silc_hash_table_alloc(1, silc_hash_ptr, NULL, NULL,
1492                                              NULL, NULL, NULL, TRUE);
1493   if (!channel->user_list) {
1494     silc_free(channel->channel_name);
1495     silc_free(channel);
1496     return NULL;
1497   }
1498
1499   /* Normalize channel name */
1500   channel_namec = silc_channel_name_check(channel_name, strlen(channel_name),
1501                                           SILC_STRING_UTF8, 256, NULL);
1502   if (!channel_namec) {
1503     silc_free(channel->channel_name);
1504     silc_hash_table_free(channel->user_list);
1505     silc_free(channel);
1506     return NULL;
1507   }
1508
1509   silc_mutex_lock(conn->internal->lock);
1510
1511   /* Add channel to cache, the normalized channel name is saved to cache */
1512   if (!silc_idcache_add(conn->internal->channel_cache, channel_namec,
1513                         &channel->id, channel)) {
1514     silc_free(channel_namec);
1515     silc_free(channel->channel_name);
1516     silc_hash_table_free(channel->user_list);
1517     silc_free(channel);
1518     silc_mutex_unlock(conn->internal->lock);
1519     return NULL;
1520   }
1521
1522   silc_mutex_unlock(conn->internal->lock);
1523   silc_client_ref_channel(client, conn, channel);
1524
1525   SILC_LOG_DEBUG(("Added %p", channel));
1526
1527   return channel;
1528 }
1529
1530 /* Removes channel from the cache by the channel entry. */
1531
1532 SilcBool silc_client_del_channel(SilcClient client, SilcClientConnection conn,
1533                                  SilcChannelEntry channel)
1534 {
1535   SilcIDCacheEntry id_cache;
1536   SilcBool ret;
1537   SilcCipher key;
1538   SilcHmac hmac;
1539
1540   if (!channel)
1541     return FALSE;
1542
1543   if (silc_atomic_sub_int16(&channel->internal.refcnt, 1) > 0)
1544     return FALSE;
1545
1546   SILC_LOG_DEBUG(("Deleting channel %p", channel));
1547
1548   silc_mutex_lock(conn->internal->lock);
1549   if (silc_idcache_find_by_context(conn->internal->channel_cache, channel,
1550                                    &id_cache))
1551     silc_free(id_cache->name);
1552   ret = silc_idcache_del_by_context(conn->internal->channel_cache,
1553                                     channel, NULL);
1554   silc_mutex_unlock(conn->internal->lock);
1555
1556   if (!ret)
1557     return FALSE;
1558
1559   silc_client_empty_channel(client, conn, channel);
1560   silc_hash_table_free(channel->user_list);
1561   silc_free(channel->channel_name);
1562   silc_free(channel->topic);
1563   if (channel->founder_key)
1564     silc_pkcs_public_key_free(channel->founder_key);
1565   if (channel->internal.send_key)
1566     silc_cipher_free(channel->internal.send_key);
1567   if (channel->internal.receive_key)
1568     silc_cipher_free(channel->internal.receive_key);
1569   if (channel->internal.hmac)
1570     silc_hmac_free(channel->internal.hmac);
1571   if (channel->internal.old_channel_keys) {
1572     silc_dlist_start(channel->internal.old_channel_keys);
1573     while ((key = silc_dlist_get(channel->internal.old_channel_keys)))
1574       silc_cipher_free(key);
1575     silc_dlist_uninit(channel->internal.old_channel_keys);
1576   }
1577   if (channel->internal.old_hmacs) {
1578     silc_dlist_start(channel->internal.old_hmacs);
1579     while ((hmac = silc_dlist_get(channel->internal.old_hmacs)))
1580       silc_hmac_free(hmac);
1581     silc_dlist_uninit(channel->internal.old_hmacs);
1582   }
1583   if (channel->channel_pubkeys)
1584     silc_argument_list_free(channel->channel_pubkeys,
1585                             SILC_ARGUMENT_PUBLIC_KEY);
1586   silc_client_del_channel_private_keys(client, conn, channel);
1587   silc_atomic_uninit16(&channel->internal.refcnt);
1588   silc_rwlock_free(channel->internal.lock);
1589   silc_schedule_task_del_by_context(conn->client->schedule, channel);
1590   silc_free(channel);
1591
1592   return ret;
1593 }
1594
1595 /* Replaces the channel ID of the `channel' to `new_id'. Returns FALSE
1596    if the ID could not be changed.  This handles entry locking internally. */
1597
1598 SilcBool silc_client_replace_channel_id(SilcClient client,
1599                                         SilcClientConnection conn,
1600                                         SilcChannelEntry channel,
1601                                         SilcChannelID *new_id)
1602 {
1603   SilcBool ret = FALSE;
1604
1605   if (!new_id)
1606     return FALSE;
1607
1608   SILC_LOG_DEBUG(("Old Channel ID id(%s)",
1609                   silc_id_render(&channel->id, SILC_ID_CHANNEL)));
1610   SILC_LOG_DEBUG(("New Channel ID id(%s)",
1611                   silc_id_render(new_id, SILC_ID_CHANNEL)));
1612
1613   /* Update the ID */
1614   silc_rwlock_wrlock(channel->internal.lock);
1615   silc_mutex_lock(conn->internal->lock);
1616   silc_idcache_update_by_context(conn->internal->channel_cache, channel,
1617                                  new_id, NULL, FALSE);
1618   silc_mutex_unlock(conn->internal->lock);
1619   silc_rwlock_unlock(channel->internal.lock);
1620
1621   return ret;
1622 }
1623
1624 /* Lock channel */
1625
1626 void silc_client_lock_channel(SilcChannelEntry channel_entry)
1627 {
1628   silc_rwlock_rdlock(channel_entry->internal.lock);
1629 }
1630
1631 /* Unlock channel */
1632
1633 void silc_client_unlock_channel(SilcChannelEntry channel_entry)
1634 {
1635   silc_rwlock_unlock(channel_entry->internal.lock);
1636 }
1637
1638 /* Take reference of channel entry */
1639
1640 SilcChannelEntry silc_client_ref_channel(SilcClient client,
1641                                          SilcClientConnection conn,
1642                                          SilcChannelEntry channel_entry)
1643 {
1644   silc_atomic_add_int16(&channel_entry->internal.refcnt, 1);
1645   SILC_LOG_DEBUG(("Channel %p refcnt %d->%d", channel_entry,
1646                   silc_atomic_get_int16(&channel_entry->internal.refcnt) - 1,
1647                   silc_atomic_get_int16(&channel_entry->internal.refcnt)));
1648   return channel_entry;
1649 }
1650
1651 /* Release reference of channel entry */
1652
1653 void silc_client_unref_channel(SilcClient client, SilcClientConnection conn,
1654                                SilcChannelEntry channel_entry)
1655 {
1656   if (channel_entry) {
1657     SILC_LOG_DEBUG(("Channel %p refcnt %d->%d", channel_entry,
1658                     silc_atomic_get_int16(&channel_entry->internal.refcnt),
1659                     silc_atomic_get_int16(&channel_entry->internal.refcnt)
1660                     - 1));
1661     silc_client_del_channel(client, conn, channel_entry);
1662   }
1663 }
1664
1665 /* Free channel entry list */
1666
1667 void silc_client_list_free_channels(SilcClient client,
1668                                     SilcClientConnection conn,
1669                                     SilcDList channel_list)
1670 {
1671   SilcChannelEntry channel_entry;
1672
1673   if (channel_list) {
1674     silc_dlist_start(channel_list);
1675     while ((channel_entry = silc_dlist_get(channel_list)))
1676       silc_client_unref_channel(client, conn, channel_entry);
1677
1678     silc_dlist_uninit(channel_list);
1679   }
1680 }
1681
1682 /************************* Server Searching Locally *************************/
1683
1684 /* Finds entry for server by the server name. */
1685
1686 SilcServerEntry silc_client_get_server(SilcClient client,
1687                                        SilcClientConnection conn,
1688                                        char *server_name)
1689 {
1690   SilcIDCacheEntry id_cache;
1691   SilcServerEntry entry;
1692
1693   if (!client || !conn || !server_name)
1694     return NULL;
1695
1696   SILC_LOG_DEBUG(("Find server by name %s", server_name));
1697
1698   /* Normalize server name for search */
1699   server_name = silc_identifier_check(server_name, strlen(server_name),
1700                                       SILC_STRING_UTF8, 256, NULL);
1701   if (!server_name)
1702     return NULL;
1703
1704   silc_mutex_lock(conn->internal->lock);
1705
1706   if (!silc_idcache_find_by_name_one(conn->internal->server_cache,
1707                                      server_name, &id_cache)) {
1708     silc_free(server_name);
1709     silc_mutex_unlock(conn->internal->lock);
1710     return NULL;
1711   }
1712
1713   SILC_LOG_DEBUG(("Found"));
1714
1715   /* Reference */
1716   entry = id_cache->context;
1717   silc_client_ref_server(client, conn, entry);
1718
1719   silc_mutex_unlock(conn->internal->lock);
1720
1721   silc_free(server_name);
1722
1723   return entry;
1724 }
1725
1726 /* Finds entry for server by the server ID. */
1727
1728 SilcServerEntry silc_client_get_server_by_id(SilcClient client,
1729                                              SilcClientConnection conn,
1730                                              SilcServerID *server_id)
1731 {
1732   SilcIDCacheEntry id_cache;
1733   SilcServerEntry entry;
1734
1735   if (!client || !conn || !server_id)
1736     return NULL;
1737
1738   SILC_LOG_DEBUG(("Find server by id %s",
1739                   silc_id_render(server_id, SILC_ID_SERVER)));
1740
1741   silc_mutex_lock(conn->internal->lock);
1742
1743   if (!silc_idcache_find_by_id_one(conn->internal->server_cache,
1744                                    server_id, &id_cache)) {
1745     silc_mutex_unlock(conn->internal->lock);
1746     return NULL;
1747   }
1748
1749   SILC_LOG_DEBUG(("Found"));
1750
1751   /* Reference */
1752   entry = id_cache->context;
1753   silc_client_ref_server(client, conn, entry);
1754
1755   silc_mutex_unlock(conn->internal->lock);
1756
1757   return entry;
1758 }
1759
1760 /*********************** Server Resolving from Server ***********************/
1761
1762 /* Resolving context */
1763 typedef struct {
1764   SilcDList servers;
1765   SilcGetServerCallback completion;
1766   void *context;
1767 } *SilcClientGetServerInternal;
1768
1769 /* Resolving command callback */
1770
1771 static SilcBool silc_client_get_server_cb(SilcClient client,
1772                                           SilcClientConnection conn,
1773                                           SilcCommand command,
1774                                           SilcStatus status,
1775                                           SilcStatus error,
1776                                           void *context,
1777                                           va_list ap)
1778 {
1779   SilcClientGetServerInternal i = context;
1780   SilcServerEntry server;
1781
1782   if (error != SILC_STATUS_OK) {
1783     SILC_LOG_DEBUG(("Resolving failed: %s", silc_get_status_message(error)));
1784     if (i->completion)
1785       i->completion(client, conn, error, NULL, i->context);
1786     goto out;
1787   }
1788
1789   /* Add the returned servers to list */
1790   if (i->completion) {
1791     server = va_arg(ap, SilcServerEntry);
1792     silc_client_ref_server(client, conn, server);
1793     silc_dlist_add(i->servers, server);
1794     server->internal.resolve_cmd_ident = 0;
1795   }
1796
1797   if (status == SILC_STATUS_OK || status == SILC_STATUS_LIST_END) {
1798     /* Deliver the servers to the caller */
1799     if (i->completion) {
1800       SILC_LOG_DEBUG(("Resolved %d servers", silc_dlist_count(i->servers)));
1801       silc_dlist_start(i->servers);
1802       i->completion(client, conn, SILC_STATUS_OK, i->servers, i->context);
1803     }
1804     goto out;
1805   }
1806
1807   return TRUE;
1808
1809  out:
1810   silc_client_list_free_servers(client, conn, i->servers);
1811   silc_free(i);
1812   return FALSE;
1813 }
1814
1815 /* Resolve server by server ID */
1816
1817 SilcUInt16
1818 silc_client_get_server_by_id_resolve(SilcClient client,
1819                                      SilcClientConnection conn,
1820                                      SilcServerID *server_id,
1821                                      SilcGetServerCallback completion,
1822                                      void *context)
1823 {
1824   SilcClientGetServerInternal i;
1825   SilcServerEntry server;
1826   SilcBuffer idp;
1827   SilcUInt16 cmd_ident;
1828
1829   if (!client || !conn || !server_id || !completion)
1830     return 0;
1831
1832   SILC_LOG_DEBUG(("Resolve server by id %s",
1833                   silc_id_render(server_id, SILC_ID_SERVER)));
1834
1835   i = silc_calloc(1, sizeof(*i));
1836   if (!i)
1837     return 0;
1838   i->completion = completion;
1839   i->context = context;
1840   i->servers = silc_dlist_init();
1841   if (!i->servers) {
1842     silc_free(i);
1843     return 0;
1844   }
1845
1846   /* Attach to resolving, if on going */
1847   server = silc_client_get_server_by_id(client, conn, server_id);
1848   if (server && server->internal.resolve_cmd_ident) {
1849     SILC_LOG_DEBUG(("Attach to existing resolving"));
1850     silc_client_unref_server(client, conn, server);
1851     silc_client_command_pending(conn, SILC_COMMAND_NONE,
1852                                 server->internal.resolve_cmd_ident,
1853                                 silc_client_get_server_cb, i);
1854     return server->internal.resolve_cmd_ident;
1855   }
1856
1857   /* Send the command */
1858   idp = silc_id_payload_encode(server_id, SILC_ID_SERVER);
1859   cmd_ident = silc_client_command_send(client, conn, SILC_COMMAND_IDENTIFY,
1860                                        silc_client_get_server_cb, i, 1,
1861                                        5, silc_buffer_datalen(idp));
1862   silc_buffer_free(idp);
1863   if (!cmd_ident && completion)
1864     completion(client, conn, SILC_STATUS_ERR_RESOURCE_LIMIT, NULL, context);
1865
1866   if (server && cmd_ident)
1867     server->internal.resolve_cmd_ident = cmd_ident;
1868
1869   silc_client_unref_server(client, conn, server);
1870
1871   return cmd_ident;
1872 }
1873
1874 /************************** Server Entry Routines ***************************/
1875
1876 /* Add new server entry */
1877
1878 SilcServerEntry silc_client_add_server(SilcClient client,
1879                                        SilcClientConnection conn,
1880                                        const char *server_name,
1881                                        const char *server_info,
1882                                        SilcServerID *server_id)
1883 {
1884   SilcServerEntry server_entry;
1885   char *server_namec = NULL;
1886
1887   if (!server_id)
1888     return NULL;
1889
1890   SILC_LOG_DEBUG(("Adding new server %s", server_name));
1891
1892   server_entry = silc_calloc(1, sizeof(*server_entry));
1893   if (!server_entry)
1894     return NULL;
1895
1896   silc_rwlock_alloc(&server_entry->internal.lock);
1897   silc_atomic_init8(&server_entry->internal.refcnt, 0);
1898   server_entry->id = *server_id;
1899   if (server_name)
1900     server_entry->server_name = strdup(server_name);
1901   if (server_info)
1902     server_entry->server_info = strdup(server_info);
1903
1904   /* Normalize server name */
1905   if (server_name) {
1906     server_namec = silc_identifier_check(server_name, strlen(server_name),
1907                                          SILC_STRING_UTF8, 256, NULL);
1908     if (!server_namec) {
1909       silc_free(server_entry->server_name);
1910       silc_free(server_entry->server_info);
1911       silc_free(server_entry);
1912       return NULL;
1913     }
1914   }
1915
1916   silc_mutex_lock(conn->internal->lock);
1917
1918   /* Add server to cache */
1919   if (!silc_idcache_add(conn->internal->server_cache, server_namec,
1920                         &server_entry->id, server_entry)) {
1921     silc_free(server_namec);
1922     silc_free(server_entry->server_name);
1923     silc_free(server_entry->server_info);
1924     silc_free(server_entry);
1925     silc_mutex_unlock(conn->internal->lock);
1926     return NULL;
1927   }
1928
1929   silc_mutex_unlock(conn->internal->lock);
1930   silc_client_ref_server(client, conn, server_entry);
1931
1932   SILC_LOG_DEBUG(("Added %p", server_entry));
1933
1934   return server_entry;
1935 }
1936
1937 /* Removes server from the cache by the server entry. */
1938
1939 SilcBool silc_client_del_server(SilcClient client, SilcClientConnection conn,
1940                                 SilcServerEntry server)
1941 {
1942   SilcIDCacheEntry id_cache;
1943   SilcBool ret;
1944
1945   if (!server)
1946     return FALSE;
1947
1948   if (silc_atomic_sub_int8(&server->internal.refcnt, 1) > 0)
1949     return FALSE;
1950
1951   SILC_LOG_DEBUG(("Deleting server %p", server));
1952
1953   silc_mutex_lock(conn->internal->lock);
1954   if (silc_idcache_find_by_context(conn->internal->server_cache, server,
1955                                    &id_cache))
1956     silc_free(id_cache->name);
1957   ret = silc_idcache_del_by_context(conn->internal->server_cache,
1958                                     server, NULL);
1959   silc_mutex_unlock(conn->internal->lock);
1960
1961   silc_free(server->server_name);
1962   silc_free(server->server_info);
1963   if (server->public_key)
1964     silc_pkcs_public_key_free(server->public_key);
1965   silc_atomic_uninit8(&server->internal.refcnt);
1966   silc_rwlock_free(server->internal.lock);
1967   silc_free(server);
1968
1969   return ret;
1970 }
1971
1972 /* Updates the `server_entry' with the new information sent as argument. */
1973
1974 void silc_client_update_server(SilcClient client,
1975                                SilcClientConnection conn,
1976                                SilcServerEntry server_entry,
1977                                const char *server_name,
1978                                const char *server_info)
1979 {
1980   char *server_namec = NULL;
1981
1982   SILC_LOG_DEBUG(("Updating server %p", server_entry));
1983
1984   if (server_name &&
1985       (!server_entry->server_name ||
1986        !silc_utf8_strcasecmp(server_entry->server_name, server_name))) {
1987
1988     server_namec = silc_identifier_check(server_name, strlen(server_name),
1989                                          SILC_STRING_UTF8, 256, NULL);
1990     if (!server_namec)
1991       return;
1992
1993     silc_free(server_entry->server_name);
1994     server_entry->server_name = strdup(server_name);
1995     if (!server_entry->server_name)
1996       return;
1997
1998     /* Update cache entry */
1999     silc_mutex_lock(conn->internal->lock);
2000     silc_idcache_update_by_context(conn->internal->server_cache, server_entry,
2001                                    NULL, server_namec, TRUE);
2002     silc_mutex_unlock(conn->internal->lock);
2003   }
2004
2005   if (server_info &&
2006       (!server_entry->server_info ||
2007        !silc_utf8_strcasecmp(server_entry->server_info, server_info))) {
2008     silc_free(server_entry->server_info);
2009     server_entry->server_info = strdup(server_info);
2010   }
2011 }
2012
2013 /* Lock server */
2014
2015 void silc_client_lock_server(SilcServerEntry server_entry)
2016 {
2017   silc_rwlock_rdlock(server_entry->internal.lock);
2018 }
2019
2020 /* Unlock server */
2021
2022 void silc_client_unlock_server(SilcServerEntry server_entry)
2023 {
2024   silc_rwlock_unlock(server_entry->internal.lock);
2025 }
2026
2027 /* Take reference of server entry */
2028
2029 SilcServerEntry silc_client_ref_server(SilcClient client,
2030                                        SilcClientConnection conn,
2031                                        SilcServerEntry server_entry)
2032 {
2033   silc_atomic_add_int8(&server_entry->internal.refcnt, 1);
2034   SILC_LOG_DEBUG(("Server %p refcnt %d->%d", server_entry,
2035                   silc_atomic_get_int8(&server_entry->internal.refcnt) - 1,
2036                   silc_atomic_get_int8(&server_entry->internal.refcnt)));
2037   return server_entry;
2038 }
2039
2040 /* Release reference of server entry */
2041
2042 void silc_client_unref_server(SilcClient client, SilcClientConnection conn,
2043                               SilcServerEntry server_entry)
2044 {
2045   if (server_entry) {
2046     SILC_LOG_DEBUG(("Server %p refcnt %d->%d", server_entry,
2047                     silc_atomic_get_int8(&server_entry->internal.refcnt),
2048                     silc_atomic_get_int8(&server_entry->internal.refcnt)
2049                     - 1));
2050     silc_client_del_server(client, conn, server_entry);
2051   }
2052 }
2053
2054 /* Free server entry list */
2055
2056 void silc_client_list_free_servers(SilcClient client,
2057                                    SilcClientConnection conn,
2058                                    SilcDList server_list)
2059 {
2060   SilcServerEntry server_entry;
2061
2062   if (server_list) {
2063     silc_dlist_start(server_list);
2064     while ((server_entry = silc_dlist_get(server_list)))
2065       silc_client_unref_server(client, conn, server_entry);
2066
2067     silc_dlist_uninit(server_list);
2068   }
2069 }