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