SILC_FSM_* macro API changes.
[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_atomic_init8(&client_entry->internal.refcnt, 0);
700   client_entry->id = *id;
701   client_entry->internal.valid = TRUE;
702   client_entry->mode = mode;
703   client_entry->realname = userinfo ? strdup(userinfo) : NULL;
704   silc_parse_userfqdn(nickname, client_entry->nickname,
705                       sizeof(client_entry->nickname),
706                       client_entry->server,
707                       sizeof(client_entry->server));
708   silc_parse_userfqdn(username, client_entry->username,
709                       sizeof(client_entry->username),
710                       client_entry->hostname,
711                       sizeof(client_entry->hostname));
712   client_entry->channels = silc_hash_table_alloc(1, silc_hash_ptr, NULL, NULL,
713                                                  NULL, NULL, NULL, TRUE);
714   if (!client_entry->channels) {
715     silc_free(client_entry->realname);
716     silc_free(client_entry);
717     return NULL;
718   }
719
720   /* Normalize nickname */
721   if (client_entry->nickname[0]) {
722     nick = silc_identifier_check(client_entry->nickname,
723                                  strlen(client_entry->nickname),
724                                  SILC_STRING_UTF8, 128, NULL);
725     if (!nick) {
726       silc_free(client_entry->realname);
727       silc_hash_table_free(client_entry->channels);
728       silc_free(client_entry);
729       return NULL;
730     }
731   }
732
733   silc_mutex_lock(conn->internal->lock);
734
735   /* Add client to cache, the normalized nickname is saved to cache */
736   if (!silc_idcache_add(conn->internal->client_cache, nick,
737                         &client_entry->id, client_entry)) {
738     silc_free(nick);
739     silc_free(client_entry->realname);
740     silc_hash_table_free(client_entry->channels);
741     silc_free(client_entry);
742     silc_mutex_unlock(conn->internal->lock);
743     return NULL;
744   }
745
746   client_entry->nickname_normalized = nick;
747
748   silc_mutex_unlock(conn->internal->lock);
749   silc_client_ref_client(client, conn, client_entry);
750
751   /* Format the nickname */
752   silc_client_nickname_format(client, conn, client_entry, FALSE);
753
754   SILC_LOG_DEBUG(("Added %p", client_entry));
755
756   return client_entry;
757 }
758
759 /* Updates the `client_entry' with the new information sent as argument. */
760
761 void silc_client_update_client(SilcClient client,
762                                SilcClientConnection conn,
763                                SilcClientEntry client_entry,
764                                const char *nickname,
765                                const char *username,
766                                const char *userinfo,
767                                SilcUInt32 mode)
768 {
769   char *nick = NULL;
770
771   SILC_LOG_DEBUG(("Update client entry"));
772
773   if (!client_entry->realname && userinfo)
774     client_entry->realname = strdup(userinfo);
775   if ((!client_entry->username[0] || !client_entry->hostname[0]) && username)
776     silc_parse_userfqdn(username, client_entry->username,
777                         sizeof(client_entry->username),
778                         client_entry->hostname,
779                         sizeof(client_entry->username));
780   if (!client_entry->nickname[0] && nickname) {
781     silc_parse_userfqdn(nickname, client_entry->nickname,
782                         sizeof(client_entry->nickname),
783                         client_entry->server,
784                         sizeof(client_entry->server));
785
786     /* Normalize nickname */
787     nick = silc_identifier_check(client_entry->nickname,
788                                  strlen(client_entry->nickname),
789                                  SILC_STRING_UTF8, 128, NULL);
790     if (!nick)
791       return;
792
793     /* Format nickname */
794     silc_client_nickname_format(client, conn, client_entry,
795                                 client_entry == conn->local_entry);
796
797     /* Update cache entry */
798     silc_mutex_lock(conn->internal->lock);
799     silc_idcache_update_by_context(conn->internal->client_cache,
800                                    client_entry, NULL, nick, TRUE);
801     silc_mutex_unlock(conn->internal->lock);
802     client_entry->nickname_normalized = nick;
803   }
804   client_entry->mode = mode;
805 }
806
807 /* Change a client's nickname */
808
809 SilcBool silc_client_change_nickname(SilcClient client,
810                                      SilcClientConnection conn,
811                                      SilcClientEntry client_entry,
812                                      const char *new_nick,
813                                      SilcClientID *new_id,
814                                      const unsigned char *idp,
815                                      SilcUInt32 idp_len)
816 {
817   char *tmp;
818
819   SILC_LOG_DEBUG(("Change nickname %s to %s", client_entry->nickname,
820                   new_nick));
821
822   /* Normalize nickname */
823   tmp = silc_identifier_check(new_nick, strlen(new_nick),
824                               SILC_STRING_UTF8, 128, NULL);
825   if (!tmp)
826     return FALSE;
827
828   /* Update the client entry */
829   silc_mutex_lock(conn->internal->lock);
830   if (!silc_idcache_update_by_context(conn->internal->client_cache,
831                                       client_entry, new_id, tmp, TRUE)) {
832     silc_free(tmp);
833     silc_mutex_unlock(conn->internal->lock);
834     return FALSE;
835   }
836   silc_mutex_unlock(conn->internal->lock);
837
838   memset(client_entry->nickname, 0, sizeof(client_entry->nickname));
839   memcpy(client_entry->nickname, new_nick, strlen(new_nick));
840   client_entry->nickname_normalized = tmp;
841   silc_client_nickname_format(client, conn, client_entry,
842                               client_entry == conn->local_entry);
843
844   /* For my client entry, update ID and set new ID to packet stream */
845   if (client_entry == conn->local_entry) {
846     if (idp && idp_len) {
847       silc_buffer_enlarge(conn->internal->local_idp, idp_len);
848       silc_buffer_put(conn->internal->local_idp, idp, idp_len);
849     }
850     if (new_id)
851       silc_packet_set_ids(conn->stream, SILC_ID_CLIENT, conn->local_id,
852                           0, NULL);
853   }
854
855   return TRUE;
856 }
857
858 /* Deletes the client entry and frees all memory. */
859
860 void silc_client_del_client_entry(SilcClient client,
861                                   SilcClientConnection conn,
862                                   SilcClientEntry client_entry)
863 {
864   silc_free(client_entry->realname);
865   silc_free(client_entry->nickname_normalized);
866   silc_free(client_entry->internal.key);
867   if (client_entry->public_key)
868     silc_pkcs_public_key_free(client_entry->public_key);
869   silc_hash_table_free(client_entry->channels);
870   if (client_entry->internal.send_key)
871     silc_cipher_free(client_entry->internal.send_key);
872   if (client_entry->internal.receive_key)
873     silc_cipher_free(client_entry->internal.receive_key);
874   if (client_entry->internal.hmac_send)
875     silc_hmac_free(client_entry->internal.hmac_send);
876   if (client_entry->internal.hmac_receive)
877     silc_hmac_free(client_entry->internal.hmac_receive);
878 #if 0
879   silc_client_ftp_session_free_client(conn, client_entry);
880   if (client_entry->internal->ke)
881     silc_client_abort_key_agreement(client, conn, client_entry);
882 #endif /* 0 */
883   silc_atomic_uninit8(&client_entry->internal.refcnt);
884   silc_free(client_entry);
885 }
886
887 /* Removes client from the cache by the client entry. */
888
889 SilcBool silc_client_del_client(SilcClient client, SilcClientConnection conn,
890                                 SilcClientEntry client_entry)
891 {
892   SilcBool ret;
893
894   if (!client_entry)
895     return FALSE;
896
897   if (silc_atomic_sub_int8(&client_entry->internal.refcnt, 1) > 0)
898     return FALSE;
899
900   SILC_LOG_DEBUG(("Deleting client %p", client_entry));
901
902   silc_mutex_lock(conn->internal->lock);
903   ret = silc_idcache_del_by_context(conn->internal->client_cache,
904                                     client_entry, NULL);
905   silc_mutex_unlock(conn->internal->lock);
906
907   if (ret) {
908     /* Remove from channels */
909     silc_client_remove_from_channels(client, conn, client_entry);
910
911     /* Free the client entry data */
912     silc_client_del_client_entry(client, conn, client_entry);
913   }
914
915   return ret;
916 }
917
918 /* Take reference of client entry */
919
920 void silc_client_ref_client(SilcClient client, SilcClientConnection conn,
921                             SilcClientEntry client_entry)
922 {
923   silc_atomic_add_int8(&client_entry->internal.refcnt, 1);
924   SILC_LOG_DEBUG(("Client %p refcnt %d->%d", client_entry,
925                   silc_atomic_get_int8(&client_entry->internal.refcnt) - 1,
926                   silc_atomic_get_int8(&client_entry->internal.refcnt)));
927 }
928
929 /* Release reference of client entry */
930
931 void silc_client_unref_client(SilcClient client, SilcClientConnection conn,
932                               SilcClientEntry client_entry)
933 {
934   if (client_entry) {
935     SILC_LOG_DEBUG(("Client %p refcnt %d->%d", client_entry,
936                     silc_atomic_get_int8(&client_entry->internal.refcnt),
937                     silc_atomic_get_int8(&client_entry->internal.refcnt) - 1));
938     silc_client_del_client(client, conn, client_entry);
939   }
940 }
941
942 /* Free client entry list */
943
944 void silc_client_list_free(SilcClient client, SilcClientConnection conn,
945                            SilcDList client_list)
946 {
947   SilcClientEntry client_entry;
948
949   if (client_list) {
950     silc_dlist_start(client_list);
951     while ((client_entry = silc_dlist_get(client_list)))
952       silc_client_unref_client(client, conn, client_entry);
953
954     silc_dlist_uninit(client_list);
955   }
956 }
957
958 /* Formats the nickname of the client specified by the `client_entry'.
959    If the format is specified by the application this will format the
960    nickname and replace the old nickname in the client entry. If the
961    format string is not specified then this function has no effect.
962    Returns the client entry that was formatted. */
963
964 SilcClientEntry silc_client_nickname_format(SilcClient client,
965                                             SilcClientConnection conn,
966                                             SilcClientEntry client_entry,
967                                             SilcBool priority)
968 {
969   char *cp;
970   char newnick[128 + 1];
971   int i, off = 0, len;
972   SilcBool freebase;
973   SilcDList clients;
974   SilcClientEntry entry, unformatted = NULL;
975
976   SILC_LOG_DEBUG(("Format nickname"));
977
978   if (!client->internal->params->nickname_format[0])
979     return client_entry;
980   if (!client_entry->nickname[0])
981     return NULL;
982
983   /* Get all clients with same nickname. Do not perform the formatting
984      if there aren't any clients with same nickname unless the application
985      is forcing us to do so. */
986   clients = silc_client_get_clients_local(client, conn,
987                                           client_entry->nickname, NULL);
988   if (!clients)
989     return NULL;
990   if (silc_dlist_count(clients) == 1 &&
991       !client->internal->params->nickname_force_format) {
992     silc_client_list_free(client, conn, clients);
993     return client_entry;
994   }
995
996   len = 0;
997   freebase = TRUE;
998   while ((entry = silc_dlist_get(clients))) {
999     if (entry->internal.valid && entry != client_entry)
1000       len++;
1001     if (entry->internal.valid && entry != client_entry &&
1002         silc_utf8_strcasecmp(entry->nickname, client_entry->nickname)) {
1003       freebase = FALSE;
1004       unformatted = entry;
1005       break;
1006     }
1007   }
1008   if (!len || freebase) {
1009     silc_client_list_free(client, conn, clients);
1010     return client_entry;
1011   }
1012
1013   /* If priority formatting, this client always gets unformatted nickname. */
1014   if (unformatted && priority)
1015     client_entry = unformatted;
1016
1017   memset(newnick, 0, sizeof(newnick));
1018   cp = client->internal->params->nickname_format;
1019   while (cp && *cp) {
1020     if (*cp == '%') {
1021       cp++;
1022       continue;
1023     }
1024
1025     switch(*cp) {
1026     case 'n':
1027       /* Nickname */
1028       if (!client_entry->nickname[0])
1029         break;
1030       len = strlen(client_entry->nickname);
1031       memcpy(&newnick[off], client_entry->nickname, len);
1032       off += len;
1033       break;
1034     case 'h':
1035       /* Stripped hostname */
1036       if (!client_entry->hostname[0])
1037         break;
1038       len = strcspn(client_entry->hostname, ".");
1039       i = strcspn(client_entry->hostname, "-");
1040       if (i < len)
1041         len = i;
1042       memcpy(&newnick[off], client_entry->hostname, len);
1043       off += len;
1044       break;
1045     case 'H':
1046       /* Full hostname */
1047       if (!client_entry->hostname[0])
1048         break;
1049       len = strlen(client_entry->hostname);
1050       memcpy(&newnick[off], client_entry->hostname, len);
1051       off += len;
1052       break;
1053     case 's':
1054       /* Stripped server name */
1055       if (!client_entry->server)
1056         break;
1057       len = strcspn(client_entry->server, ".");
1058       memcpy(&newnick[off], client_entry->server, len);
1059       off += len;
1060       break;
1061     case 'S':
1062       /* Full server name */
1063       if (!client_entry->server)
1064         break;
1065       len = strlen(client_entry->server);
1066       memcpy(&newnick[off], client_entry->server, len);
1067       off += len;
1068       break;
1069     case 'a':
1070       /* Ascending number */
1071       {
1072         char tmp[6];
1073         int num, max = 1;
1074
1075         if (silc_dlist_count(clients) == 1)
1076           break;
1077
1078         silc_dlist_start(clients);
1079         while ((entry = silc_dlist_get(clients))) {
1080           if (!silc_utf8_strncasecmp(entry->nickname, newnick, off))
1081             continue;
1082           if (strlen(entry->nickname) <= off)
1083             continue;
1084           num = atoi(&entry->nickname[off]);
1085           if (num > max)
1086             max = num;
1087         }
1088
1089         memset(tmp, 0, sizeof(tmp));
1090         silc_snprintf(tmp, sizeof(tmp) - 1, "%d", ++max);
1091         len = strlen(tmp);
1092         memcpy(&newnick[off], tmp, len);
1093         off += len;
1094       }
1095       break;
1096     default:
1097       /* Some other character in the string */
1098       memcpy(&newnick[off], cp, 1);
1099       off++;
1100       break;
1101     }
1102
1103     cp++;
1104   }
1105
1106   newnick[off] = 0;
1107   memcpy(client_entry->nickname, newnick, strlen(newnick));
1108   silc_client_list_free(client, conn, clients);
1109
1110   return client_entry;
1111 }
1112
1113 /* Parses nickname according to nickname format string */
1114
1115 SilcBool silc_client_nickname_parse(SilcClient client,
1116                                     SilcClientConnection conn,
1117                                     char *nickname,
1118                                     char **ret_nick)
1119 {
1120   char *cp, s = 0, e = 0, *nick;
1121   SilcBool n = FALSE;
1122   int len;
1123
1124   if (!client->internal->params->nickname_format[0])
1125     return TRUE;
1126
1127   if (!nickname || !nickname[0])
1128     return FALSE;
1129
1130   cp = client->internal->params->nickname_format;
1131   while (cp && *cp) {
1132     if (*cp == '%') {
1133       cp++;
1134       continue;
1135     }
1136
1137     switch(*cp) {
1138     case 'n':
1139       n = TRUE;
1140       break;
1141
1142     case 'h':
1143     case 'H':
1144     case 's':
1145     case 'S':
1146     case 'a':
1147       break;
1148
1149     default:
1150       /* Get separator character */
1151       if (n)
1152         e = *cp;
1153       else
1154         s = *cp;
1155       break;
1156     }
1157
1158      cp++;
1159   }
1160   if (!n)
1161     return FALSE;
1162
1163   /* Parse the nickname */
1164   nick = nickname;
1165   len = strlen(nick);
1166   if (s)
1167     if (strchr(nickname, s))
1168       nick = strchr(nickname, s) + 1;
1169   if (e)
1170     if (strchr(nick, e))
1171       len = strchr(nick, e) - nick;
1172   if (!len)
1173     return FALSE;
1174
1175   *ret_nick = silc_memdup(nick, len);
1176   if (!(*ret_nick))
1177     return FALSE;
1178
1179   SILC_LOG_DEBUG(("Parsed nickname: %s", *ret_nick));
1180
1181   return TRUE;
1182 }
1183
1184 /************************ Channel Searching Locally *************************/
1185
1186 /* Finds entry for channel by the channel name. Returns the entry or NULL
1187    if the entry was not found. It is found only if the client is joined
1188    to the channel. */
1189
1190 SilcChannelEntry silc_client_get_channel(SilcClient client,
1191                                          SilcClientConnection conn,
1192                                          char *channel)
1193 {
1194   SilcIDCacheEntry id_cache;
1195   SilcChannelEntry entry;
1196
1197   if (!client || !conn || !channel)
1198     return NULL;
1199
1200   SILC_LOG_DEBUG(("Find channel %s", channel));
1201
1202   /* Normalize name for search */
1203   channel = silc_channel_name_check(channel, strlen(channel), SILC_STRING_UTF8,
1204                                     256, NULL);
1205   if (!channel)
1206     return NULL;
1207
1208   silc_mutex_lock(conn->internal->lock);
1209
1210   if (!silc_idcache_find_by_name_one(conn->internal->channel_cache, channel,
1211                                      &id_cache)) {
1212     silc_mutex_unlock(conn->internal->lock);
1213     silc_free(channel);
1214     return NULL;
1215   }
1216
1217   SILC_LOG_DEBUG(("Found"));
1218
1219   entry = id_cache->context;
1220
1221   /* Reference */
1222   silc_client_ref_channel(client, conn, entry);
1223   silc_mutex_unlock(conn->internal->lock);
1224
1225   silc_free(channel);
1226
1227   return entry;
1228 }
1229
1230 /* Finds entry for channel by the channel ID. Returns the entry or NULL
1231    if the entry was not found. It is found only if the client is joined
1232    to the channel. */
1233
1234 SilcChannelEntry silc_client_get_channel_by_id(SilcClient client,
1235                                                SilcClientConnection conn,
1236                                                SilcChannelID *channel_id)
1237 {
1238   SilcIDCacheEntry id_cache;
1239   SilcChannelEntry entry;
1240
1241   if (!client || !conn || !channel_id)
1242     return NULL;
1243
1244   SILC_LOG_DEBUG(("Find channel by id %s",
1245                   silc_id_render(channel_id, SILC_ID_CHANNEL)));
1246
1247   silc_mutex_lock(conn->internal->lock);
1248
1249   if (!silc_idcache_find_by_id_one(conn->internal->channel_cache, channel_id,
1250                                    &id_cache)) {
1251     silc_mutex_unlock(conn->internal->lock);
1252     return NULL;
1253   }
1254
1255   SILC_LOG_DEBUG(("Found"));
1256
1257   entry = id_cache->context;
1258
1259   /* Reference */
1260   silc_client_ref_channel(client, conn, entry);
1261   silc_mutex_unlock(conn->internal->lock);
1262
1263   return entry;
1264 }
1265
1266 /********************** Channel Resolving from Server ***********************/
1267
1268 /* Channel resolving context */
1269 typedef struct {
1270   SilcDList channels;
1271   SilcGetChannelCallback completion;
1272   void *context;
1273 } *SilcClientGetChannelInternal;
1274
1275 /* Resolving command callback */
1276
1277 static SilcBool silc_client_get_channel_cb(SilcClient client,
1278                                            SilcClientConnection conn,
1279                                            SilcCommand command,
1280                                            SilcStatus status,
1281                                            SilcStatus error,
1282                                            void *context,
1283                                            va_list ap)
1284 {
1285   SilcClientGetChannelInternal i = context;
1286   SilcChannelEntry entry;
1287
1288   if (error != SILC_STATUS_OK) {
1289     SILC_LOG_DEBUG(("Resolving failed: %s", silc_get_status_message(error)));
1290     if (i->completion)
1291       i->completion(client, conn, error, NULL, i->context);
1292     goto out;
1293   }
1294
1295   /* Add the returned channel to list */
1296   if (i->completion) {
1297     entry = va_arg(ap, SilcChannelEntry);
1298     silc_client_ref_channel(client, conn, entry);
1299     silc_dlist_add(i->channels, entry);
1300   }
1301
1302   if (status == SILC_STATUS_OK || status == SILC_STATUS_LIST_END) {
1303     /* Deliver the channels to the caller */
1304     if (i->completion) {
1305       SILC_LOG_DEBUG(("Resolved %d channels", silc_dlist_count(i->channels)));
1306       silc_dlist_start(i->channels);
1307       i->completion(client, conn, SILC_STATUS_OK, i->channels, i->context);
1308     }
1309     goto out;
1310   }
1311
1312   return TRUE;
1313
1314  out:
1315   silc_client_list_free_channels(client, conn, i->channels);
1316   silc_free(i);
1317   return FALSE;
1318 }
1319
1320 /* Resolves channel entry from the server by the channel name. */
1321
1322 void silc_client_get_channel_resolve(SilcClient client,
1323                                      SilcClientConnection conn,
1324                                      char *channel_name,
1325                                      SilcGetChannelCallback completion,
1326                                      void *context)
1327 {
1328   SilcClientGetChannelInternal i;
1329
1330   if (!client || !conn || !channel_name || !completion)
1331     return;
1332
1333   SILC_LOG_DEBUG(("Resolve channel %s", channel_name));
1334
1335   i = silc_calloc(1, sizeof(*i));
1336   if (!i)
1337     return;
1338   i->completion = completion;
1339   i->context = context;
1340   i->channels = silc_dlist_init();
1341   if (!i->channels) {
1342     silc_free(i);
1343     return;
1344   }
1345
1346   /* Send the command */
1347   if (!silc_client_command_send(client, conn, SILC_COMMAND_IDENTIFY,
1348                                 silc_client_get_channel_cb, i, 1,
1349                                 3, channel_name, strlen(channel_name))) {
1350     if (completion)
1351       completion(client, conn, SILC_STATUS_ERR_RESOURCE_LIMIT, NULL, context);
1352   }
1353 }
1354
1355 /* Resolves channel information from the server by the channel ID. */
1356
1357 SilcUInt16
1358 silc_client_get_channel_by_id_resolve(SilcClient client,
1359                                       SilcClientConnection conn,
1360                                       SilcChannelID *channel_id,
1361                                       SilcGetChannelCallback completion,
1362                                       void *context)
1363 {
1364   SilcClientGetChannelInternal i;
1365   SilcBuffer idp;
1366   SilcUInt16 cmd_ident;
1367
1368   if (!client || !conn || !channel_id || !completion)
1369     return 0;
1370
1371   SILC_LOG_DEBUG(("Resolve channel by id %s",
1372                   silc_id_render(channel_id, SILC_ID_CHANNEL)));
1373
1374   i = silc_calloc(1, sizeof(*i));
1375   if (!i)
1376     return 0;
1377   i->completion = completion;
1378   i->context = context;
1379   i->channels = silc_dlist_init();
1380   if (!i->channels) {
1381     silc_free(i);
1382     return 0;
1383   }
1384
1385   /* Send the command */
1386   idp = silc_id_payload_encode(channel_id, SILC_ID_CHANNEL);
1387   cmd_ident = silc_client_command_send(client, conn, SILC_COMMAND_IDENTIFY,
1388                                        silc_client_get_channel_cb, i, 1,
1389                                        5, silc_buffer_datalen(idp));
1390   silc_buffer_free(idp);
1391   if (!cmd_ident && completion)
1392     completion(client, conn, SILC_STATUS_ERR_RESOURCE_LIMIT, NULL, context);
1393
1394   return cmd_ident;
1395 }
1396
1397 /************************* Channel Entry Routines ***************************/
1398
1399 /* Add new channel entry to the ID Cache */
1400
1401 SilcChannelEntry silc_client_add_channel(SilcClient client,
1402                                          SilcClientConnection conn,
1403                                          const char *channel_name,
1404                                          SilcUInt32 mode,
1405                                          SilcChannelID *channel_id)
1406 {
1407   SilcChannelEntry channel;
1408   char *channel_namec;
1409
1410   SILC_LOG_DEBUG(("Start"));
1411
1412   channel = silc_calloc(1, sizeof(*channel));
1413   if (!channel)
1414     return NULL;
1415
1416   silc_atomic_init16(&channel->internal.refcnt, 0);
1417   channel->id = *channel_id;
1418   channel->mode = mode;
1419
1420   channel->channel_name = strdup(channel_name);
1421   if (!channel->channel_name) {
1422     silc_free(channel);
1423     return NULL;
1424   }
1425
1426   channel->user_list = silc_hash_table_alloc(1, silc_hash_ptr, NULL, NULL,
1427                                              NULL, NULL, NULL, TRUE);
1428   if (!channel->user_list) {
1429     silc_free(channel->channel_name);
1430     silc_free(channel);
1431     return NULL;
1432   }
1433
1434   /* Normalize channel name */
1435   channel_namec = silc_channel_name_check(channel_name, strlen(channel_name),
1436                                           SILC_STRING_UTF8, 256, NULL);
1437   if (!channel_namec) {
1438     silc_free(channel->channel_name);
1439     silc_hash_table_free(channel->user_list);
1440     silc_free(channel);
1441     return NULL;
1442   }
1443
1444   silc_mutex_lock(conn->internal->lock);
1445
1446   /* Add channel to cache, the normalized channel name is saved to cache */
1447   if (!silc_idcache_add(conn->internal->channel_cache, channel_namec,
1448                         &channel->id, channel)) {
1449     silc_free(channel_namec);
1450     silc_free(channel->channel_name);
1451     silc_hash_table_free(channel->user_list);
1452     silc_free(channel);
1453     silc_mutex_unlock(conn->internal->lock);
1454     return NULL;
1455   }
1456
1457   silc_mutex_unlock(conn->internal->lock);
1458   silc_client_ref_channel(client, conn, channel);
1459
1460   SILC_LOG_DEBUG(("Added %p", channel));
1461
1462   return channel;
1463 }
1464
1465 /* Removes channel from the cache by the channel entry. */
1466
1467 SilcBool silc_client_del_channel(SilcClient client, SilcClientConnection conn,
1468                                  SilcChannelEntry channel)
1469 {
1470   SilcBool ret;
1471   SilcCipher key;
1472   SilcHmac hmac;
1473
1474   if (!channel)
1475     return FALSE;
1476
1477   if (silc_atomic_sub_int16(&channel->internal.refcnt, 1) > 0)
1478     return FALSE;
1479
1480   SILC_LOG_DEBUG(("Deleting channel %p", channel));
1481
1482   silc_mutex_lock(conn->internal->lock);
1483   ret = silc_idcache_del_by_context(conn->internal->channel_cache,
1484                                     channel, NULL);
1485   silc_mutex_unlock(conn->internal->lock);
1486
1487   if (!ret)
1488     return FALSE;
1489
1490   silc_client_empty_channel(client, conn, channel);
1491   silc_hash_table_free(channel->user_list);
1492   silc_free(channel->channel_name);
1493   silc_free(channel->topic);
1494   if (channel->founder_key)
1495     silc_pkcs_public_key_free(channel->founder_key);
1496   if (channel->internal.send_key)
1497     silc_cipher_free(channel->internal.send_key);
1498   if (channel->internal.receive_key)
1499     silc_cipher_free(channel->internal.receive_key);
1500   if (channel->internal.hmac)
1501     silc_hmac_free(channel->internal.hmac);
1502   if (channel->internal.old_channel_keys) {
1503     silc_dlist_start(channel->internal.old_channel_keys);
1504     while ((key = silc_dlist_get(channel->internal.old_channel_keys)))
1505       silc_cipher_free(key);
1506     silc_dlist_uninit(channel->internal.old_channel_keys);
1507   }
1508   if (channel->internal.old_hmacs) {
1509     silc_dlist_start(channel->internal.old_hmacs);
1510     while ((hmac = silc_dlist_get(channel->internal.old_hmacs)))
1511       silc_hmac_free(hmac);
1512     silc_dlist_uninit(channel->internal.old_hmacs);
1513   }
1514   if (channel->channel_pubkeys)
1515     silc_argument_list_free(channel->channel_pubkeys,
1516                             SILC_ARGUMENT_PUBLIC_KEY);
1517   silc_client_del_channel_private_keys(client, conn, channel);
1518   silc_atomic_uninit16(&channel->internal.refcnt);
1519   silc_schedule_task_del_by_context(conn->client->schedule, channel);
1520   silc_free(channel);
1521
1522   return ret;
1523 }
1524
1525 /* Replaces the channel ID of the `channel' to `new_id'. Returns FALSE
1526    if the ID could not be changed. */
1527
1528 SilcBool silc_client_replace_channel_id(SilcClient client,
1529                                         SilcClientConnection conn,
1530                                         SilcChannelEntry channel,
1531                                         SilcChannelID *new_id)
1532 {
1533   SilcBool ret = FALSE;
1534
1535   if (!new_id)
1536     return FALSE;
1537
1538   SILC_LOG_DEBUG(("Old Channel ID id(%s)",
1539                   silc_id_render(&channel->id, SILC_ID_CHANNEL)));
1540   SILC_LOG_DEBUG(("New Channel ID id(%s)",
1541                   silc_id_render(new_id, SILC_ID_CHANNEL)));
1542
1543   /* Update the ID */
1544   silc_mutex_lock(conn->internal->lock);
1545   silc_idcache_update_by_context(conn->internal->channel_cache, channel,
1546                                  new_id, NULL, FALSE);
1547   silc_mutex_unlock(conn->internal->lock);
1548
1549   return ret;
1550 }
1551
1552 /* Take reference of channel entry */
1553
1554 void silc_client_ref_channel(SilcClient client, SilcClientConnection conn,
1555                              SilcChannelEntry channel_entry)
1556 {
1557   silc_atomic_add_int16(&channel_entry->internal.refcnt, 1);
1558   SILC_LOG_DEBUG(("Channel %p refcnt %d->%d", channel_entry,
1559                   silc_atomic_get_int16(&channel_entry->internal.refcnt) - 1,
1560                   silc_atomic_get_int16(&channel_entry->internal.refcnt)));
1561 }
1562
1563 /* Release reference of channel entry */
1564
1565 void silc_client_unref_channel(SilcClient client, SilcClientConnection conn,
1566                                SilcChannelEntry channel_entry)
1567 {
1568   if (channel_entry) {
1569     SILC_LOG_DEBUG(("Channel %p refcnt %d->%d", channel_entry,
1570                     silc_atomic_get_int16(&channel_entry->internal.refcnt),
1571                     silc_atomic_get_int16(&channel_entry->internal.refcnt)
1572                     - 1));
1573     silc_client_del_channel(client, conn, channel_entry);
1574   }
1575 }
1576
1577 /* Free channel entry list */
1578
1579 void silc_client_list_free_channels(SilcClient client,
1580                                     SilcClientConnection conn,
1581                                     SilcDList channel_list)
1582 {
1583   SilcChannelEntry channel_entry;
1584
1585   if (channel_list) {
1586     silc_dlist_start(channel_list);
1587     while ((channel_entry = silc_dlist_get(channel_list)))
1588       silc_client_unref_channel(client, conn, channel_entry);
1589
1590     silc_dlist_uninit(channel_list);
1591   }
1592 }
1593
1594 /************************* Server Searching Locally *************************/
1595
1596 /* Finds entry for server by the server name. */
1597
1598 SilcServerEntry silc_client_get_server(SilcClient client,
1599                                        SilcClientConnection conn,
1600                                        char *server_name)
1601 {
1602   SilcIDCacheEntry id_cache;
1603   SilcServerEntry entry;
1604
1605   if (!client || !conn || !server_name)
1606     return NULL;
1607
1608   SILC_LOG_DEBUG(("Find server by name %s", server_name));
1609
1610   /* Normalize server name for search */
1611   server_name = silc_identifier_check(server_name, strlen(server_name),
1612                                       SILC_STRING_UTF8, 256, NULL);
1613   if (!server_name)
1614     return NULL;
1615
1616   silc_mutex_lock(conn->internal->lock);
1617
1618   if (!silc_idcache_find_by_name_one(conn->internal->server_cache,
1619                                      server_name, &id_cache)) {
1620     silc_free(server_name);
1621     silc_mutex_unlock(conn->internal->lock);
1622     return NULL;
1623   }
1624
1625   SILC_LOG_DEBUG(("Found"));
1626
1627   /* Reference */
1628   entry = id_cache->context;
1629   silc_client_ref_server(client, conn, entry);
1630
1631   silc_mutex_unlock(conn->internal->lock);
1632
1633   silc_free(server_name);
1634
1635   return entry;
1636 }
1637
1638 /* Finds entry for server by the server ID. */
1639
1640 SilcServerEntry silc_client_get_server_by_id(SilcClient client,
1641                                              SilcClientConnection conn,
1642                                              SilcServerID *server_id)
1643 {
1644   SilcIDCacheEntry id_cache;
1645   SilcServerEntry entry;
1646
1647   if (!client || !conn || !server_id)
1648     return NULL;
1649
1650   SILC_LOG_DEBUG(("Find server by id %s",
1651                   silc_id_render(server_id, SILC_ID_SERVER)));
1652
1653   silc_mutex_lock(conn->internal->lock);
1654
1655   if (!silc_idcache_find_by_id_one(conn->internal->server_cache,
1656                                    server_id, &id_cache)) {
1657     silc_mutex_unlock(conn->internal->lock);
1658     return NULL;
1659   }
1660
1661   SILC_LOG_DEBUG(("Found"));
1662
1663   /* Reference */
1664   entry = id_cache->context;
1665   silc_client_ref_server(client, conn, entry);
1666
1667   silc_mutex_unlock(conn->internal->lock);
1668
1669   return entry;
1670 }
1671
1672 /*********************** Server Resolving from Server ***********************/
1673
1674 /* Resolving context */
1675 typedef struct {
1676   SilcDList servers;
1677   SilcGetServerCallback completion;
1678   void *context;
1679 } *SilcClientGetServerInternal;
1680
1681 /* Resolving command callback */
1682
1683 static SilcBool silc_client_get_server_cb(SilcClient client,
1684                                           SilcClientConnection conn,
1685                                           SilcCommand command,
1686                                           SilcStatus status,
1687                                           SilcStatus error,
1688                                           void *context,
1689                                           va_list ap)
1690 {
1691   SilcClientGetServerInternal i = context;
1692   SilcServerEntry server;
1693
1694   if (error != SILC_STATUS_OK) {
1695     SILC_LOG_DEBUG(("Resolving failed: %s", silc_get_status_message(error)));
1696     if (i->completion)
1697       i->completion(client, conn, error, NULL, i->context);
1698     goto out;
1699   }
1700
1701   /* Add the returned servers to list */
1702   if (i->completion) {
1703     server = va_arg(ap, SilcServerEntry);
1704     silc_client_ref_server(client, conn, server);
1705     silc_dlist_add(i->servers, server);
1706     server->internal.resolve_cmd_ident = 0;
1707   }
1708
1709   if (status == SILC_STATUS_OK || status == SILC_STATUS_LIST_END) {
1710     /* Deliver the servers to the caller */
1711     if (i->completion) {
1712       SILC_LOG_DEBUG(("Resolved %d servers", silc_dlist_count(i->servers)));
1713       silc_dlist_start(i->servers);
1714       i->completion(client, conn, SILC_STATUS_OK, i->servers, i->context);
1715     }
1716     goto out;
1717   }
1718
1719   return TRUE;
1720
1721  out:
1722   silc_client_list_free_servers(client, conn, i->servers);
1723   silc_free(i);
1724   return FALSE;
1725 }
1726
1727 /* Resolve server by server ID */
1728
1729 SilcUInt16
1730 silc_client_get_server_by_id_resolve(SilcClient client,
1731                                      SilcClientConnection conn,
1732                                      SilcServerID *server_id,
1733                                      SilcGetServerCallback completion,
1734                                      void *context)
1735 {
1736   SilcClientGetServerInternal i;
1737   SilcServerEntry server;
1738   SilcBuffer idp;
1739   SilcUInt16 cmd_ident;
1740
1741   if (!client || !conn || !server_id || !completion)
1742     return 0;
1743
1744   SILC_LOG_DEBUG(("Resolve server by id %s",
1745                   silc_id_render(server_id, SILC_ID_SERVER)));
1746
1747   i = silc_calloc(1, sizeof(*i));
1748   if (!i)
1749     return 0;
1750   i->completion = completion;
1751   i->context = context;
1752   i->servers = silc_dlist_init();
1753   if (!i->servers) {
1754     silc_free(i);
1755     return 0;
1756   }
1757
1758   /* Attach to resolving, if on going */
1759   server = silc_client_get_server_by_id(client, conn, server_id);
1760   if (server && server->internal.resolve_cmd_ident) {
1761     SILC_LOG_DEBUG(("Attach to existing resolving"));
1762     silc_client_unref_server(client, conn, server);
1763     silc_client_command_pending(conn, SILC_COMMAND_NONE,
1764                                 server->internal.resolve_cmd_ident,
1765                                 silc_client_get_server_cb, i);
1766     return server->internal.resolve_cmd_ident;
1767   }
1768
1769   /* Send the command */
1770   idp = silc_id_payload_encode(server_id, SILC_ID_SERVER);
1771   cmd_ident = silc_client_command_send(client, conn, SILC_COMMAND_IDENTIFY,
1772                                        silc_client_get_server_cb, i, 1,
1773                                        5, silc_buffer_datalen(idp));
1774   silc_buffer_free(idp);
1775   if (!cmd_ident && completion)
1776     completion(client, conn, SILC_STATUS_ERR_RESOURCE_LIMIT, NULL, context);
1777
1778   if (server && cmd_ident)
1779     server->internal.resolve_cmd_ident = cmd_ident;
1780
1781   silc_client_unref_server(client, conn, server);
1782
1783   return cmd_ident;
1784 }
1785
1786 /************************** Server Entry Routines ***************************/
1787
1788 /* Add new server entry */
1789
1790 SilcServerEntry silc_client_add_server(SilcClient client,
1791                                        SilcClientConnection conn,
1792                                        const char *server_name,
1793                                        const char *server_info,
1794                                        SilcServerID *server_id)
1795 {
1796   SilcServerEntry server_entry;
1797   char *server_namec = NULL;
1798
1799   if (!server_id)
1800     return NULL;
1801
1802   SILC_LOG_DEBUG(("Adding new server %s", server_name));
1803
1804   server_entry = silc_calloc(1, sizeof(*server_entry));
1805   if (!server_entry)
1806     return NULL;
1807
1808   silc_atomic_init8(&server_entry->internal.refcnt, 0);
1809   server_entry->id = *server_id;
1810   if (server_name)
1811     server_entry->server_name = strdup(server_name);
1812   if (server_info)
1813     server_entry->server_info = strdup(server_info);
1814
1815   /* Normalize server name */
1816   if (server_name) {
1817     server_namec = silc_identifier_check(server_name, strlen(server_name),
1818                                          SILC_STRING_UTF8, 256, NULL);
1819     if (!server_namec) {
1820       silc_free(server_entry->server_name);
1821       silc_free(server_entry->server_info);
1822       silc_free(server_entry);
1823       return NULL;
1824     }
1825   }
1826
1827   silc_mutex_lock(conn->internal->lock);
1828
1829   /* Add server to cache */
1830   if (!silc_idcache_add(conn->internal->server_cache, server_namec,
1831                         &server_entry->id, server_entry)) {
1832     silc_free(server_namec);
1833     silc_free(server_entry->server_name);
1834     silc_free(server_entry->server_info);
1835     silc_free(server_entry);
1836     silc_mutex_unlock(conn->internal->lock);
1837     return NULL;
1838   }
1839
1840   silc_mutex_unlock(conn->internal->lock);
1841   silc_client_ref_server(client, conn, server_entry);
1842
1843   SILC_LOG_DEBUG(("Added %p", server_entry));
1844
1845   return server_entry;
1846 }
1847
1848 /* Removes server from the cache by the server entry. */
1849
1850 SilcBool silc_client_del_server(SilcClient client, SilcClientConnection conn,
1851                                 SilcServerEntry server)
1852 {
1853   SilcBool ret;
1854
1855   if (!server)
1856     return FALSE;
1857
1858   if (silc_atomic_sub_int8(&server->internal.refcnt, 1) > 0)
1859     return FALSE;
1860
1861   SILC_LOG_DEBUG(("Deleting server %p", server));
1862
1863   silc_mutex_lock(conn->internal->lock);
1864   ret = silc_idcache_del_by_context(conn->internal->server_cache,
1865                                     server, NULL);
1866   silc_mutex_unlock(conn->internal->lock);
1867
1868   silc_free(server->server_name);
1869   silc_free(server->server_info);
1870   if (server->public_key)
1871     silc_pkcs_public_key_free(server->public_key);
1872   silc_atomic_uninit8(&server->internal.refcnt);
1873   silc_free(server);
1874
1875   return ret;
1876 }
1877
1878 /* Updates the `server_entry' with the new information sent as argument. */
1879
1880 void silc_client_update_server(SilcClient client,
1881                                SilcClientConnection conn,
1882                                SilcServerEntry server_entry,
1883                                const char *server_name,
1884                                const char *server_info)
1885 {
1886   char *server_namec = NULL;
1887
1888   SILC_LOG_DEBUG(("Updating server %p", server_entry));
1889
1890   if (server_name &&
1891       (!server_entry->server_name ||
1892        !silc_utf8_strcasecmp(server_entry->server_name, server_name))) {
1893
1894     server_namec = silc_identifier_check(server_name, strlen(server_name),
1895                                          SILC_STRING_UTF8, 256, NULL);
1896     if (!server_namec)
1897       return;
1898
1899     silc_free(server_entry->server_name);
1900     server_entry->server_name = strdup(server_name);
1901     if (!server_entry->server_name)
1902       return;
1903
1904     /* Update cache entry */
1905     silc_mutex_lock(conn->internal->lock);
1906     silc_idcache_update_by_context(conn->internal->server_cache, server_entry,
1907                                    NULL, server_namec, TRUE);
1908     silc_mutex_unlock(conn->internal->lock);
1909   }
1910
1911   if (server_info &&
1912       (!server_entry->server_info ||
1913        !silc_utf8_strcasecmp(server_entry->server_info, server_info))) {
1914     silc_free(server_entry->server_info);
1915     server_entry->server_info = strdup(server_info);
1916   }
1917 }
1918
1919 /* Take reference of server entry */
1920
1921 void silc_client_ref_server(SilcClient client, SilcClientConnection conn,
1922                             SilcServerEntry server_entry)
1923 {
1924   silc_atomic_add_int8(&server_entry->internal.refcnt, 1);
1925   SILC_LOG_DEBUG(("Server %p refcnt %d->%d", server_entry,
1926                   silc_atomic_get_int8(&server_entry->internal.refcnt) - 1,
1927                   silc_atomic_get_int8(&server_entry->internal.refcnt)));
1928 }
1929
1930 /* Release reference of server entry */
1931
1932 void silc_client_unref_server(SilcClient client, SilcClientConnection conn,
1933                               SilcServerEntry server_entry)
1934 {
1935   if (server_entry) {
1936     SILC_LOG_DEBUG(("Server %p refcnt %d->%d", server_entry,
1937                     silc_atomic_get_int8(&server_entry->internal.refcnt),
1938                     silc_atomic_get_int8(&server_entry->internal.refcnt)
1939                     - 1));
1940     silc_client_del_server(client, conn, server_entry);
1941   }
1942 }
1943
1944 /* Free server entry list */
1945
1946 void silc_client_list_free_servers(SilcClient client,
1947                                    SilcClientConnection conn,
1948                                    SilcDList server_list)
1949 {
1950   SilcServerEntry server_entry;
1951
1952   if (server_list) {
1953     silc_dlist_start(server_list);
1954     while ((server_entry = silc_dlist_get(server_list)))
1955       silc_client_unref_server(client, conn, server_entry);
1956
1957     silc_dlist_uninit(server_list);
1958   }
1959 }