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