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