5 Author: Pekka Riikonen <priikone@silcnet.org>
7 Copyright (C) 2001 - 2007 Pekka Riikonen
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.
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.
22 #include "silcclient.h"
23 #include "client_internal.h"
25 /************************ Client Searching Locally **************************/
27 /* Finds entry for client by the client's ID. Returns the entry or NULL
28 if the entry was not found. */
30 SilcClientEntry silc_client_get_client_by_id(SilcClient client,
31 SilcClientConnection conn,
32 SilcClientID *client_id)
34 SilcIDCacheEntry id_cache;
35 SilcClientEntry client_entry;
37 if (!client || !conn || !client_id)
40 SILC_LOG_DEBUG(("Finding client by ID (%s)",
41 silc_id_render(client_id, SILC_ID_CLIENT)));
43 silc_mutex_lock(conn->internal->lock);
45 /* Find ID from cache */
46 if (!silc_idcache_find_by_id_one(conn->internal->client_cache, client_id,
48 silc_mutex_unlock(conn->internal->lock);
52 client_entry = id_cache->context;
55 silc_client_ref_client(client, conn, client_entry);
56 silc_mutex_unlock(conn->internal->lock);
58 SILC_LOG_DEBUG(("Found"));
63 /* Finds clients by nickname from local cache. */
65 SilcDList silc_client_get_clients_local_ext(SilcClient client,
66 SilcClientConnection conn,
71 SilcIDCacheEntry id_cache;
74 SilcClientEntry entry;
75 char *nicknamec, *parsed = NULL, *format = NULL;
77 if (!client || !conn || !nickname)
80 /* Parse nickname if it is formatted */
81 if (!silc_client_nickname_parse(client, conn, (char *)nickname, &parsed))
84 if (!get_all && parsed)
85 format = (char *)nickname;
87 parsed = silc_memdup(nickname, strlen(nickname));
92 SILC_LOG_DEBUG(("Find clients by nickname %s", parsed));
94 /* Normalize nickname for search */
95 nicknamec = silc_identifier_check(parsed, strlen(parsed),
96 SILC_STRING_UTF8, 128, NULL);
102 clients = silc_dlist_init();
104 silc_free(nicknamec);
109 silc_mutex_lock(conn->internal->lock);
111 /* Find from cache */
112 silc_list_init(list, struct SilcIDCacheEntryStruct, next);
113 if (!silc_idcache_find_by_name(conn->internal->client_cache, nicknamec,
115 silc_mutex_unlock(conn->internal->lock);
116 silc_free(nicknamec);
118 silc_dlist_uninit(clients);
121 silc_list_start(list);
123 if (!format && get_all) {
124 /* Take all without any further checking */
125 while ((id_cache = silc_list_get(list))) {
126 entry = id_cache->context;
127 if (!get_valid || entry->internal.valid) {
128 silc_client_ref_client(client, conn, id_cache->context);
129 silc_dlist_add(clients, id_cache->context);
133 /* Check multiple cache entries for exact match */
134 while ((id_cache = silc_list_get(list))) {
135 entry = id_cache->context;
136 if (silc_utf8_strcasecmp(entry->nickname,
137 format ? format : parsed) &&
138 (!get_valid || entry->internal.valid)) {
139 silc_client_ref_client(client, conn, entry);
140 silc_dlist_add(clients, entry);
142 /* If format is NULL, we find one exact match with the base
143 nickname (parsed). */
150 silc_mutex_unlock(conn->internal->lock);
152 silc_free(nicknamec);
155 if (!silc_dlist_count(clients)) {
156 silc_dlist_uninit(clients);
160 silc_dlist_start(clients);
164 /* Finds clients by nickname from local cache. */
166 SilcDList silc_client_get_clients_local(SilcClient client,
167 SilcClientConnection conn,
168 const char *nickname,
171 return silc_client_get_clients_local_ext(client, conn, nickname, return_all,
175 /********************** Client Resolving from Server ************************/
177 /* Resolving context */
180 SilcGetClientCallback completion;
182 SilcClientEntry client_entry;
183 } *SilcClientGetClientInternal;
185 /* Resolving command callback */
187 static SilcBool silc_client_get_clients_cb(SilcClient client,
188 SilcClientConnection conn,
195 SilcClientGetClientInternal i = context;
196 SilcClientEntry client_entry;
198 if (error != SILC_STATUS_OK) {
199 SILC_LOG_DEBUG(("Resolving failed: %s", silc_get_status_message(error)));
201 if (i->client_entry) {
202 i->client_entry->internal.resolve_cmd_ident = 0;
203 silc_client_unref_client(client, conn, i->client_entry);
207 i->completion(client, conn, error, NULL, i->context);
211 /* Add the returned client to list */
213 client_entry = va_arg(ap, SilcClientEntry);
214 silc_client_ref_client(client, conn, client_entry);
215 silc_dlist_add(i->clients, client_entry);
216 client_entry->internal.resolve_cmd_ident = 0;
219 if (status == SILC_STATUS_OK || status == SILC_STATUS_LIST_END) {
220 /* Deliver the clients to the caller */
222 SILC_LOG_DEBUG(("Resolved %d clients", silc_dlist_count(i->clients)));
224 if (i->client_entry) {
225 i->client_entry->internal.resolve_cmd_ident = 0;
226 silc_client_unref_client(client, conn, i->client_entry);
229 silc_dlist_start(i->clients);
230 i->completion(client, conn, SILC_STATUS_OK, i->clients, i->context);
238 silc_client_list_free(client, conn, i->clients);
243 /* Resolves client information from server by the client ID. */
246 silc_client_get_client_by_id_resolve(SilcClient client,
247 SilcClientConnection conn,
248 SilcClientID *client_id,
249 SilcBuffer attributes,
250 SilcGetClientCallback completion,
253 SilcClientGetClientInternal i;
254 SilcClientEntry client_entry;
256 SilcUInt16 cmd_ident;
258 if (!client || !conn | !client_id)
261 SILC_LOG_DEBUG(("Resolve client by ID (%s)",
262 silc_id_render(client_id, SILC_ID_CLIENT)));
264 i = silc_calloc(1, sizeof(*i));
267 i->completion = completion;
268 i->context = context;
269 i->clients = silc_dlist_init();
275 /* Attach to resolving, if on going */
276 client_entry = silc_client_get_client_by_id(client, conn, client_id);
277 if (client_entry && client_entry->internal.resolve_cmd_ident) {
278 SILC_LOG_DEBUG(("Attach to existing resolving"));
279 silc_client_unref_client(client, conn, client_entry);
280 silc_client_command_pending(conn, SILC_COMMAND_NONE,
281 client_entry->internal.resolve_cmd_ident,
282 silc_client_get_clients_cb, i);
283 return client_entry->internal.resolve_cmd_ident;
286 /* Send the command */
287 idp = silc_id_payload_encode(client_id, SILC_ID_CLIENT);
288 cmd_ident = silc_client_command_send(client, conn, SILC_COMMAND_WHOIS,
289 silc_client_get_clients_cb, i,
290 2, 3, silc_buffer_datalen(attributes),
291 4, silc_buffer_datalen(idp));
292 if (!cmd_ident && completion)
293 completion(client, conn, SILC_STATUS_ERR_RESOURCE_LIMIT, NULL, context);
295 if (client_entry && cmd_ident) {
296 client_entry->internal.resolve_cmd_ident = cmd_ident;
297 i->client_entry = client_entry;
299 silc_client_unref_client(client, conn, client_entry);
302 silc_buffer_free(idp);
307 /* Finds client entry or entries by the `nickname' and `server'. The
308 completion callback will be called when the client entries has been
309 found. Used internally by the library. */
311 static SilcUInt16 silc_client_get_clients_i(SilcClient client,
312 SilcClientConnection conn,
314 const char *nickname,
316 SilcBuffer attributes,
317 SilcGetClientCallback completion,
320 SilcClientGetClientInternal i;
321 char userhost[768 + 1];
324 SILC_LOG_DEBUG(("Resolve client by %s command",
325 silc_get_command_name(command)));
327 if (!client || !conn)
329 if (!nickname && !attributes)
332 i = silc_calloc(1, sizeof(*i));
335 i->clients = silc_dlist_init();
340 i->completion = completion;
341 i->context = context;
343 memset(userhost, 0, sizeof(userhost));
344 if (nickname && server) {
345 len = strlen(nickname) + strlen(server) + 3;
346 silc_strncat(userhost, len, nickname, strlen(nickname));
347 silc_strncat(userhost, len, "@", 1);
348 silc_strncat(userhost, len, server, strlen(server));
349 } else if (nickname) {
350 silc_strncat(userhost, sizeof(userhost) - 1, nickname, strlen(nickname));
353 /* Send the command */
354 if (command == SILC_COMMAND_IDENTIFY)
355 return silc_client_command_send(client, conn, SILC_COMMAND_IDENTIFY,
356 silc_client_get_clients_cb, i,
357 1, 1, userhost, strlen(userhost));
358 return silc_client_command_send(client, conn, SILC_COMMAND_WHOIS,
359 silc_client_get_clients_cb, i,
360 2, 1, userhost, strlen(userhost),
361 3, silc_buffer_datalen(attributes));
364 /* Get clients from server with IDENTIFY command */
366 SilcUInt16 silc_client_get_clients(SilcClient client,
367 SilcClientConnection conn,
368 const char *nickname,
370 SilcGetClientCallback completion,
373 return silc_client_get_clients_i(client, conn, SILC_COMMAND_IDENTIFY,
374 nickname, server, NULL,
375 completion, context);
378 /* Get clients from server with WHOIS command */
380 SilcUInt16 silc_client_get_clients_whois(SilcClient client,
381 SilcClientConnection conn,
382 const char *nickname,
384 SilcBuffer attributes,
385 SilcGetClientCallback completion,
388 return silc_client_get_clients_i(client, conn, SILC_COMMAND_WHOIS,
389 nickname, server, attributes,
390 completion, context);
393 /* ID list resolving context */
395 SilcGetClientCallback completion;
397 SilcBuffer client_id_list;
398 SilcUInt32 list_count;
399 } *GetClientsByListInternal;
401 static SilcBool silc_client_get_clients_list_cb(SilcClient client,
402 SilcClientConnection conn,
409 GetClientsByListInternal i = context;
410 SilcClientEntry client_entry;
416 /* Process the list after all replies have been received */
417 if (status != SILC_STATUS_OK && !SILC_STATUS_IS_ERROR(status) &&
418 status != SILC_STATUS_LIST_END)
421 SILC_LOG_DEBUG(("Resolved all clients"));
423 clients = silc_dlist_init();
425 status = SILC_STATUS_ERR_RESOURCE_LIMIT;
429 for (c = 0; c < i->list_count; c++) {
431 SILC_GET16_MSB(idp_len, i->client_id_list->data + 2);
433 if (!silc_id_payload_parse_id(i->client_id_list->data, idp_len, &id)) {
434 status = SILC_STATUS_ERR_BAD_CLIENT_ID;
438 /* Get client entry */
439 client_entry = silc_client_get_client_by_id(client, conn, &id.u.client_id);
441 silc_dlist_add(clients, client_entry);
443 if (!silc_buffer_pull(i->client_id_list, idp_len)) {
444 status = SILC_STATUS_ERR_BAD_CLIENT_ID;
449 silc_dlist_start(clients);
450 status = SILC_STATUS_OK;
452 i->completion(client, conn, status, clients, i->context);
455 if (status != SILC_STATUS_OK && i->completion)
456 i->completion(client, conn, status, NULL, i->context);
458 silc_client_list_free(client, conn, clients);
459 silc_buffer_free(i->client_id_list);
465 /* Gets client entries by the list of client ID's `client_id_list'. This
466 always resolves those client ID's it does not know yet from the server
467 so this function might take a while. The `client_id_list' is a list
468 of ID Payloads added one after other. JOIN command reply and USERS
469 command reply for example returns this sort of list. The `completion'
470 will be called after the entries are available. */
472 SilcUInt16 silc_client_get_clients_by_list(SilcClient client,
473 SilcClientConnection conn,
474 SilcUInt32 list_count,
475 SilcBuffer client_id_list,
476 SilcGetClientCallback completion,
479 GetClientsByListInternal in;
480 SilcClientEntry entry;
481 unsigned char **res_argv = NULL;
482 SilcUInt32 *res_argv_lens = NULL, *res_argv_types = NULL, res_argc = 0;
483 SilcUInt16 idp_len, cmd_ident;
488 SILC_LOG_DEBUG(("Resolve clients from Client ID list"));
490 if (!client || !conn || !client_id_list)
493 in = silc_calloc(1, sizeof(*in));
496 in->completion = completion;
497 in->context = context;
498 in->list_count = list_count;
499 in->client_id_list = silc_buffer_copy(client_id_list);
500 if (!in->client_id_list)
503 for (i = 0; i < list_count; i++) {
505 SILC_GET16_MSB(idp_len, client_id_list->data + 2);
507 if (!silc_id_payload_parse_id(client_id_list->data, idp_len, &id))
510 /* Check if we have this client cached already. If we don't have the
511 entry or it has incomplete info, then resolve it from the server. */
512 entry = silc_client_get_client_by_id(client, conn, &id.u.client_id);
513 if (!entry || !entry->nickname[0] || !entry->username[0] ||
516 res_argv = silc_calloc(list_count, sizeof(*res_argv));
517 res_argv_lens = silc_calloc(list_count, sizeof(*res_argv_lens));
518 res_argv_types = silc_calloc(list_count, sizeof(*res_argv_types));
519 if (!res_argv || !res_argv_lens || !res_argv_types) {
520 silc_client_unref_client(client, conn, entry);
525 res_argv[res_argc] = client_id_list->data;
526 res_argv_lens[res_argc] = idp_len;
527 res_argv_types[res_argc] = res_argc + 4;
530 silc_client_unref_client(client, conn, entry);
532 if (!silc_buffer_pull(client_id_list, idp_len))
535 silc_buffer_start(client_id_list);
537 /* Query the unknown client information from server */
539 cmd_ident = silc_client_command_send_argv(client,
540 conn, SILC_COMMAND_WHOIS,
541 silc_client_get_clients_list_cb,
542 in, res_argc, res_argv,
546 silc_free(res_argv_lens);
547 silc_free(res_argv_types);
551 /* We have the clients in cache, get them and call the completion */
552 silc_client_get_clients_list_cb(client, conn, SILC_COMMAND_WHOIS,
553 SILC_STATUS_OK, SILC_STATUS_OK, in, tmp);
557 silc_buffer_free(in->client_id_list);
560 silc_free(res_argv_lens);
561 silc_free(res_argv_types);
568 SilcClientConnection conn;
569 SilcChannelID channel_id;
570 SilcGetClientCallback completion;
573 } *GetClientsByChannelInternal;
575 SILC_CLIENT_CMD_FUNC(get_clients_by_channel_cb)
577 GetClientsByChannelInternal i = context;
578 SilcClientEntry *clients = NULL;
579 SilcUInt32 clients_count = 0;
580 SilcBool found = FALSE;
581 SilcChannelEntry channel;
582 SilcHashTableList htl;
591 channel = silc_client_get_channel_by_id(i->client, i->conn, &i->channel_id);
592 if (channel && !silc_hash_table_count(channel->user_list)) {
593 clients = silc_calloc(silc_hash_table_count(channel->user_list),
595 silc_hash_table_list(channel->user_list, &htl);
596 while (silc_hash_table_get(&htl, NULL, (void *)&chu))
597 clients[clients_count++] = chu->client;
598 silc_hash_table_list_reset(&htl);
603 i->completion(i->client, i->conn, clients, clients_count, i->context);
606 i->completion(i->client, i->conn, NULL, 0, i->context);
612 /* Gets client entries by the channel entry indicated by `channel'. Thus,
613 it resolves the clients currently on that channel. */
615 void silc_client_get_clients_by_channel(SilcClient client,
616 SilcClientConnection conn,
617 SilcChannelEntry channel,
618 SilcGetClientCallback completion,
621 GetClientsByChannelInternal in;
622 SilcHashTableList htl;
624 SilcClientEntry entry;
625 unsigned char **res_argv = NULL;
626 SilcUInt32 *res_argv_lens = NULL, *res_argv_types = NULL, res_argc = 0;
628 SilcBool wait_res = FALSE;
630 assert(client && conn && channel);
632 SILC_LOG_DEBUG(("Start"));
634 in = silc_calloc(1, sizeof(*in));
637 in->channel_id = *channel->id;
638 in->completion = completion;
639 in->context = context;
641 /* If user list does not exist, send USERS command. */
642 if (!channel->user_list || !silc_hash_table_count(channel->user_list)) {
643 SILC_LOG_DEBUG(("Sending USERS"));
644 silc_client_command_register(client, SILC_COMMAND_USERS, NULL, NULL,
645 silc_client_command_reply_users_i, 0,
647 silc_client_command_send(client, conn, SILC_COMMAND_USERS,
648 conn->cmd_ident, 1, 2, channel->channel_name,
649 strlen(channel->channel_name));
650 silc_client_command_pending(conn, SILC_COMMAND_USERS, conn->cmd_ident,
651 silc_client_command_get_clients_by_channel_cb,
656 silc_hash_table_list(channel->user_list, &htl);
657 while (silc_hash_table_get(&htl, NULL, (void *)&chu)) {
660 /* If the entry has incomplete info, then resolve it from the server. */
661 if (!entry->nickname[0] || !entry->realname) {
662 if (entry->status & SILC_CLIENT_STATUS_RESOLVING) {
663 /* Attach to this resolving and wait until it finishes */
664 silc_client_command_pending(
665 conn, SILC_COMMAND_NONE,
666 entry->resolve_cmd_ident,
667 silc_client_command_get_clients_by_channel_cb,
673 entry->status |= SILC_CLIENT_STATUS_RESOLVING;
674 entry->resolve_cmd_ident = conn->cmd_ident + 1;
676 idp = silc_id_payload_encode(entry->id, SILC_ID_CLIENT);
678 /* No we don't have it, query it from the server. Assemble argument
679 table that will be sent for the WHOIS command later. */
680 res_argv = silc_realloc(res_argv, sizeof(*res_argv) *
682 res_argv_lens = silc_realloc(res_argv_lens, sizeof(*res_argv_lens) *
684 res_argv_types = silc_realloc(res_argv_types, sizeof(*res_argv_types) *
686 res_argv[res_argc] = silc_memdup(idp->data, idp->len);
687 res_argv_lens[res_argc] = idp->len;
688 res_argv_types[res_argc] = res_argc + 4;
691 silc_buffer_free(idp);
694 silc_hash_table_list_reset(&htl);
696 /* Query the client information from server if the list included clients
697 that we don't know about. */
701 /* Send the WHOIS command to server */
702 res_cmd = silc_command_payload_encode(SILC_COMMAND_WHOIS,
703 res_argc, res_argv, res_argv_lens,
704 res_argv_types, ++conn->cmd_ident);
705 silc_client_packet_send(client, conn->sock, SILC_PACKET_COMMAND,
706 NULL, 0, NULL, NULL, res_cmd->data, res_cmd->len,
709 /* Register our own command reply for this command */
710 silc_client_command_register(client, SILC_COMMAND_WHOIS, NULL, NULL,
711 silc_client_command_reply_whois_i, 0,
714 /* Process the applications request after reply has been received */
715 silc_client_command_pending(
716 conn, SILC_COMMAND_WHOIS, conn->cmd_ident,
717 silc_client_command_get_clients_by_channel_cb,
721 silc_buffer_free(res_cmd);
723 silc_free(res_argv_lens);
724 silc_free(res_argv_types);
731 /* We have the clients in cache, get them and call the completion */
732 silc_client_command_get_clients_by_channel_cb((void *)in, NULL);
737 /************************** Client Entry Routines ***************************/
739 /* Creates new client entry and adds it to the ID cache. Returns pointer
742 SilcClientEntry silc_client_add_client(SilcClient client,
743 SilcClientConnection conn,
744 char *nickname, char *username,
745 char *userinfo, SilcClientID *id,
748 SilcClientEntry client_entry;
751 SILC_LOG_DEBUG(("Adding new client entry"));
753 /* Save the client infos */
754 client_entry = silc_calloc(1, sizeof(*client_entry));
758 silc_rwlock_alloc(&client_entry->internal.lock);
759 silc_atomic_init8(&client_entry->internal.refcnt, 0);
760 client_entry->id = *id;
761 client_entry->mode = mode;
762 client_entry->realname = userinfo ? strdup(userinfo) : NULL;
763 silc_parse_userfqdn(nickname, client_entry->nickname,
764 sizeof(client_entry->nickname),
765 client_entry->server,
766 sizeof(client_entry->server));
767 silc_parse_userfqdn(username, client_entry->username,
768 sizeof(client_entry->username),
769 client_entry->hostname,
770 sizeof(client_entry->hostname));
771 client_entry->channels = silc_hash_table_alloc(1, silc_hash_ptr, NULL, NULL,
772 NULL, NULL, NULL, TRUE);
773 if (!client_entry->channels) {
774 silc_free(client_entry->realname);
775 silc_free(client_entry);
779 /* Normalize nickname */
780 if (client_entry->nickname[0]) {
781 nick = silc_identifier_check(client_entry->nickname,
782 strlen(client_entry->nickname),
783 SILC_STRING_UTF8, 128, NULL);
785 silc_free(client_entry->realname);
786 silc_hash_table_free(client_entry->channels);
787 silc_free(client_entry);
792 silc_mutex_lock(conn->internal->lock);
794 /* Add client to cache, the normalized nickname is saved to cache */
795 if (!silc_idcache_add(conn->internal->client_cache, nick,
796 &client_entry->id, client_entry)) {
798 silc_free(client_entry->realname);
799 silc_hash_table_free(client_entry->channels);
800 silc_free(client_entry);
801 silc_mutex_unlock(conn->internal->lock);
805 client_entry->nickname_normalized = nick;
807 silc_mutex_unlock(conn->internal->lock);
808 silc_client_ref_client(client, conn, client_entry);
810 /* Format the nickname */
811 silc_client_nickname_format(client, conn, client_entry, FALSE);
813 if (client_entry->nickname[0])
814 client_entry->internal.valid = TRUE;
816 SILC_LOG_DEBUG(("Added %p", client_entry));
821 /* Updates the `client_entry' with the new information sent as argument.
822 This handles entry locking internally. */
824 void silc_client_update_client(SilcClient client,
825 SilcClientConnection conn,
826 SilcClientEntry client_entry,
827 const char *nickname,
828 const char *username,
829 const char *userinfo,
834 SILC_LOG_DEBUG(("Update client entry"));
836 silc_rwlock_wrlock(client_entry->internal.lock);
838 if (!client_entry->realname && userinfo)
839 client_entry->realname = strdup(userinfo);
840 if ((!client_entry->username[0] || !client_entry->hostname[0]) && username)
841 silc_parse_userfqdn(username, client_entry->username,
842 sizeof(client_entry->username),
843 client_entry->hostname,
844 sizeof(client_entry->username));
845 if (!client_entry->nickname[0] && nickname) {
846 silc_parse_userfqdn(nickname, client_entry->nickname,
847 sizeof(client_entry->nickname),
848 client_entry->server,
849 sizeof(client_entry->server));
851 /* Normalize nickname */
852 nick = silc_identifier_check(client_entry->nickname,
853 strlen(client_entry->nickname),
854 SILC_STRING_UTF8, 128, NULL);
856 silc_rwlock_unlock(client_entry->internal.lock);
860 /* Format nickname */
861 silc_client_nickname_format(client, conn, client_entry,
862 client_entry == conn->local_entry);
864 /* Update cache entry */
865 silc_mutex_lock(conn->internal->lock);
866 silc_idcache_update_by_context(conn->internal->client_cache,
867 client_entry, NULL, nick, TRUE);
868 silc_mutex_unlock(conn->internal->lock);
869 client_entry->nickname_normalized = nick;
870 client_entry->internal.valid = TRUE;
872 client_entry->mode = mode;
874 silc_rwlock_unlock(client_entry->internal.lock);
877 /* Change a client's nickname. Must be called with `client_entry' locked. */
879 SilcBool silc_client_change_nickname(SilcClient client,
880 SilcClientConnection conn,
881 SilcClientEntry client_entry,
882 const char *new_nick,
883 SilcClientID *new_id,
884 const unsigned char *idp,
889 SILC_LOG_DEBUG(("Change nickname %s to %s", client_entry->nickname,
892 /* Normalize nickname */
893 tmp = silc_identifier_check(new_nick, strlen(new_nick),
894 SILC_STRING_UTF8, 128, NULL);
898 /* Update the client entry */
899 silc_mutex_lock(conn->internal->lock);
900 if (!silc_idcache_update_by_context(conn->internal->client_cache,
901 client_entry, new_id, tmp, TRUE)) {
903 silc_mutex_unlock(conn->internal->lock);
906 silc_mutex_unlock(conn->internal->lock);
908 memset(client_entry->nickname, 0, sizeof(client_entry->nickname));
909 memcpy(client_entry->nickname, new_nick, strlen(new_nick));
910 client_entry->nickname_normalized = tmp;
911 silc_client_nickname_format(client, conn, client_entry,
912 client_entry == conn->local_entry);
914 /* For my client entry, update ID and set new ID to packet stream */
915 if (client_entry == conn->local_entry) {
916 if (idp && idp_len) {
917 silc_buffer_enlarge(conn->internal->local_idp, idp_len);
918 silc_buffer_put(conn->internal->local_idp, idp, idp_len);
921 silc_packet_set_ids(conn->stream, SILC_ID_CLIENT, conn->local_id,
925 client_entry->internal.valid = TRUE;
929 /* Deletes the client entry and frees all memory. */
931 void silc_client_del_client_entry(SilcClient client,
932 SilcClientConnection conn,
933 SilcClientEntry client_entry)
935 silc_free(client_entry->realname);
936 silc_free(client_entry->nickname_normalized);
937 silc_free(client_entry->internal.key);
938 if (client_entry->public_key)
939 silc_pkcs_public_key_free(client_entry->public_key);
940 silc_hash_table_free(client_entry->channels);
941 if (client_entry->internal.send_key)
942 silc_cipher_free(client_entry->internal.send_key);
943 if (client_entry->internal.receive_key)
944 silc_cipher_free(client_entry->internal.receive_key);
945 if (client_entry->internal.hmac_send)
946 silc_hmac_free(client_entry->internal.hmac_send);
947 if (client_entry->internal.hmac_receive)
948 silc_hmac_free(client_entry->internal.hmac_receive);
949 silc_client_ftp_session_free_client(client, client_entry);
950 if (client_entry->internal.ke)
951 silc_client_abort_key_agreement(client, conn, client_entry);
952 silc_atomic_uninit8(&client_entry->internal.refcnt);
953 silc_rwlock_free(client_entry->internal.lock);
954 silc_free(client_entry);
957 /* Removes client from the cache by the client entry. */
959 SilcBool silc_client_del_client(SilcClient client, SilcClientConnection conn,
960 SilcClientEntry client_entry)
967 if (silc_atomic_sub_int8(&client_entry->internal.refcnt, 1) > 0)
970 SILC_LOG_DEBUG(("Deleting client %p", client_entry));
972 silc_mutex_lock(conn->internal->lock);
973 ret = silc_idcache_del_by_context(conn->internal->client_cache,
975 silc_mutex_unlock(conn->internal->lock);
978 /* Remove from channels */
979 silc_client_remove_from_channels(client, conn, client_entry);
981 /* Free the client entry data */
982 silc_client_del_client_entry(client, conn, client_entry);
988 /* Internal routine used to find client by ID and if not found this creates
989 new client entry and returns it. */
991 SilcClientEntry silc_client_get_client(SilcClient client,
992 SilcClientConnection conn,
993 SilcClientID *client_id)
995 SilcClientEntry client_entry;
997 client_entry = silc_client_get_client_by_id(client, conn, client_id);
999 client_entry = silc_client_add_client(client, conn, NULL, NULL, NULL,
1003 silc_client_ref_client(client, conn, client_entry);
1006 return client_entry;
1011 void silc_client_lock_client(SilcClientEntry client_entry)
1013 silc_rwlock_rdlock(client_entry->internal.lock);
1018 void silc_client_unlock_client(SilcClientEntry client_entry)
1020 silc_rwlock_unlock(client_entry->internal.lock);
1023 /* Take reference of client entry */
1025 SilcClientEntry silc_client_ref_client(SilcClient client,
1026 SilcClientConnection conn,
1027 SilcClientEntry client_entry)
1029 silc_atomic_add_int8(&client_entry->internal.refcnt, 1);
1030 SILC_LOG_DEBUG(("Client %p refcnt %d->%d", client_entry,
1031 silc_atomic_get_int8(&client_entry->internal.refcnt) - 1,
1032 silc_atomic_get_int8(&client_entry->internal.refcnt)));
1033 return client_entry;
1036 /* Release reference of client entry */
1038 void silc_client_unref_client(SilcClient client, SilcClientConnection conn,
1039 SilcClientEntry client_entry)
1042 SILC_LOG_DEBUG(("Client %p refcnt %d->%d", client_entry,
1043 silc_atomic_get_int8(&client_entry->internal.refcnt),
1044 silc_atomic_get_int8(&client_entry->internal.refcnt) - 1));
1045 silc_client_del_client(client, conn, client_entry);
1049 /* Free client entry list */
1051 void silc_client_list_free(SilcClient client, SilcClientConnection conn,
1052 SilcDList client_list)
1054 SilcClientEntry client_entry;
1057 silc_dlist_start(client_list);
1058 while ((client_entry = silc_dlist_get(client_list)))
1059 silc_client_unref_client(client, conn, client_entry);
1061 silc_dlist_uninit(client_list);
1065 /* Formats the nickname of the client specified by the `client_entry'.
1066 If the format is specified by the application this will format the
1067 nickname and replace the old nickname in the client entry. If the
1068 format string is not specified then this function has no effect.
1069 Returns the client entry that was formatted. */
1071 SilcClientEntry silc_client_nickname_format(SilcClient client,
1072 SilcClientConnection conn,
1073 SilcClientEntry client_entry,
1077 char newnick[128 + 1];
1078 int i, off = 0, len;
1081 SilcClientEntry entry, unformatted = NULL;
1083 if (!client->internal->params->nickname_format[0])
1084 return client_entry;
1085 if (!client_entry->nickname[0])
1088 SILC_LOG_DEBUG(("Format nickname"));
1090 /* Get all clients with same nickname. Do not perform the formatting
1091 if there aren't any clients with same nickname unless the application
1092 is forcing us to do so. */
1093 clients = silc_client_get_clients_local_ext(client, conn,
1094 client_entry->nickname,
1098 if (silc_dlist_count(clients) == 1 &&
1099 !client->internal->params->nickname_force_format) {
1100 silc_client_list_free(client, conn, clients);
1101 return client_entry;
1106 while ((entry = silc_dlist_get(clients))) {
1107 if (entry->internal.valid && entry != client_entry)
1109 if (entry->internal.valid && entry != client_entry &&
1110 silc_utf8_strcasecmp(entry->nickname, client_entry->nickname)) {
1112 unformatted = entry;
1116 if (!len || freebase) {
1117 silc_client_list_free(client, conn, clients);
1118 return client_entry;
1121 /* If priority formatting, this client always gets unformatted nickname. */
1122 if (unformatted && priority)
1123 client_entry = unformatted;
1125 memset(newnick, 0, sizeof(newnick));
1126 cp = client->internal->params->nickname_format;
1136 if (!client_entry->nickname[0])
1138 len = strlen(client_entry->nickname);
1139 memcpy(&newnick[off], client_entry->nickname, len);
1143 /* Stripped hostname */
1144 if (!client_entry->hostname[0])
1146 len = strcspn(client_entry->hostname, ".");
1147 i = strcspn(client_entry->hostname, "-");
1150 memcpy(&newnick[off], client_entry->hostname, len);
1155 if (!client_entry->hostname[0])
1157 len = strlen(client_entry->hostname);
1158 memcpy(&newnick[off], client_entry->hostname, len);
1162 /* Ascending number */
1167 if (silc_dlist_count(clients) == 1)
1170 silc_dlist_start(clients);
1171 while ((entry = silc_dlist_get(clients))) {
1172 if (!silc_utf8_strncasecmp(entry->nickname, newnick, off))
1174 if (strlen(entry->nickname) <= off)
1176 num = atoi(&entry->nickname[off]);
1181 memset(tmp, 0, sizeof(tmp));
1182 silc_snprintf(tmp, sizeof(tmp) - 1, "%d", ++max);
1184 memcpy(&newnick[off], tmp, len);
1189 /* Some other character in the string */
1190 memcpy(&newnick[off], cp, 1);
1199 memcpy(client_entry->nickname, newnick, strlen(newnick));
1200 silc_client_list_free(client, conn, clients);
1202 return client_entry;
1205 /* Parses nickname according to nickname format string */
1207 SilcBool silc_client_nickname_parse(SilcClient client,
1208 SilcClientConnection conn,
1212 char *cp, s = 0, e = 0, *nick;
1216 if (!client->internal->params->nickname_format[0]) {
1221 if (!nickname || !nickname[0])
1224 cp = client->internal->params->nickname_format;
1242 /* Get separator character */
1255 /* Parse the nickname */
1259 if (strchr(nickname, s))
1260 nick = strchr(nickname, s) + 1;
1262 if (strchr(nick, e))
1263 len = strchr(nick, e) - nick;
1267 *ret_nick = silc_memdup(nick, len);
1271 SILC_LOG_DEBUG(("Parsed nickname: %s", *ret_nick));
1276 /************************ Channel Searching Locally *************************/
1278 /* Finds entry for channel by the channel name. Returns the entry or NULL
1279 if the entry was not found. It is found only if the client is joined
1282 SilcChannelEntry silc_client_get_channel(SilcClient client,
1283 SilcClientConnection conn,
1286 SilcIDCacheEntry id_cache;
1287 SilcChannelEntry entry;
1289 if (!client || !conn || !channel)
1292 SILC_LOG_DEBUG(("Find channel %s", channel));
1294 /* Normalize name for search */
1295 channel = silc_channel_name_check(channel, strlen(channel), SILC_STRING_UTF8,
1300 silc_mutex_lock(conn->internal->lock);
1302 if (!silc_idcache_find_by_name_one(conn->internal->channel_cache, channel,
1304 silc_mutex_unlock(conn->internal->lock);
1309 SILC_LOG_DEBUG(("Found"));
1311 entry = id_cache->context;
1314 silc_client_ref_channel(client, conn, entry);
1315 silc_mutex_unlock(conn->internal->lock);
1322 /* Finds entry for channel by the channel ID. Returns the entry or NULL
1323 if the entry was not found. It is found only if the client is joined
1326 SilcChannelEntry silc_client_get_channel_by_id(SilcClient client,
1327 SilcClientConnection conn,
1328 SilcChannelID *channel_id)
1330 SilcIDCacheEntry id_cache;
1331 SilcChannelEntry entry;
1333 if (!client || !conn || !channel_id)
1336 SILC_LOG_DEBUG(("Find channel by id %s",
1337 silc_id_render(channel_id, SILC_ID_CHANNEL)));
1339 silc_mutex_lock(conn->internal->lock);
1341 if (!silc_idcache_find_by_id_one(conn->internal->channel_cache, channel_id,
1343 silc_mutex_unlock(conn->internal->lock);
1347 SILC_LOG_DEBUG(("Found"));
1349 entry = id_cache->context;
1352 silc_client_ref_channel(client, conn, entry);
1353 silc_mutex_unlock(conn->internal->lock);
1358 /********************** Channel Resolving from Server ***********************/
1360 /* Channel resolving context */
1363 SilcGetChannelCallback completion;
1365 } *SilcClientGetChannelInternal;
1367 /* Resolving command callback */
1369 static SilcBool silc_client_get_channel_cb(SilcClient client,
1370 SilcClientConnection conn,
1371 SilcCommand command,
1377 SilcClientGetChannelInternal i = context;
1378 SilcChannelEntry entry;
1380 if (error != SILC_STATUS_OK) {
1381 SILC_LOG_DEBUG(("Resolving failed: %s", silc_get_status_message(error)));
1383 i->completion(client, conn, error, NULL, i->context);
1387 /* Add the returned channel to list */
1388 if (i->completion) {
1389 entry = va_arg(ap, SilcChannelEntry);
1390 silc_client_ref_channel(client, conn, entry);
1391 silc_dlist_add(i->channels, entry);
1394 if (status == SILC_STATUS_OK || status == SILC_STATUS_LIST_END) {
1395 /* Deliver the channels to the caller */
1396 if (i->completion) {
1397 SILC_LOG_DEBUG(("Resolved %d channels", silc_dlist_count(i->channels)));
1398 silc_dlist_start(i->channels);
1399 i->completion(client, conn, SILC_STATUS_OK, i->channels, i->context);
1407 silc_client_list_free_channels(client, conn, i->channels);
1412 /* Resolves channel entry from the server by the channel name. */
1414 void silc_client_get_channel_resolve(SilcClient client,
1415 SilcClientConnection conn,
1417 SilcGetChannelCallback completion,
1420 SilcClientGetChannelInternal i;
1422 if (!client || !conn || !channel_name || !completion)
1425 SILC_LOG_DEBUG(("Resolve channel %s", channel_name));
1427 i = silc_calloc(1, sizeof(*i));
1430 i->completion = completion;
1431 i->context = context;
1432 i->channels = silc_dlist_init();
1438 /* Send the command */
1439 if (!silc_client_command_send(client, conn, SILC_COMMAND_IDENTIFY,
1440 silc_client_get_channel_cb, i, 1,
1441 3, channel_name, strlen(channel_name))) {
1443 completion(client, conn, SILC_STATUS_ERR_RESOURCE_LIMIT, NULL, context);
1447 /* Resolves channel information from the server by the channel ID. */
1450 silc_client_get_channel_by_id_resolve(SilcClient client,
1451 SilcClientConnection conn,
1452 SilcChannelID *channel_id,
1453 SilcGetChannelCallback completion,
1456 SilcClientGetChannelInternal i;
1458 SilcUInt16 cmd_ident;
1460 if (!client || !conn || !channel_id || !completion)
1463 SILC_LOG_DEBUG(("Resolve channel by id %s",
1464 silc_id_render(channel_id, SILC_ID_CHANNEL)));
1466 i = silc_calloc(1, sizeof(*i));
1469 i->completion = completion;
1470 i->context = context;
1471 i->channels = silc_dlist_init();
1477 /* Send the command */
1478 idp = silc_id_payload_encode(channel_id, SILC_ID_CHANNEL);
1479 cmd_ident = silc_client_command_send(client, conn, SILC_COMMAND_IDENTIFY,
1480 silc_client_get_channel_cb, i, 1,
1481 5, silc_buffer_datalen(idp));
1482 silc_buffer_free(idp);
1483 if (!cmd_ident && completion)
1484 completion(client, conn, SILC_STATUS_ERR_RESOURCE_LIMIT, NULL, context);
1489 /************************* Channel Entry Routines ***************************/
1491 /* Add new channel entry to the ID Cache */
1493 SilcChannelEntry silc_client_add_channel(SilcClient client,
1494 SilcClientConnection conn,
1495 const char *channel_name,
1497 SilcChannelID *channel_id)
1499 SilcChannelEntry channel;
1500 char *channel_namec;
1502 SILC_LOG_DEBUG(("Start"));
1504 channel = silc_calloc(1, sizeof(*channel));
1508 silc_rwlock_alloc(&channel->internal.lock);
1509 silc_atomic_init16(&channel->internal.refcnt, 0);
1510 channel->id = *channel_id;
1511 channel->mode = mode;
1513 channel->channel_name = strdup(channel_name);
1514 if (!channel->channel_name) {
1519 channel->user_list = silc_hash_table_alloc(1, silc_hash_ptr, NULL, NULL,
1520 NULL, NULL, NULL, TRUE);
1521 if (!channel->user_list) {
1522 silc_free(channel->channel_name);
1527 /* Normalize channel name */
1528 channel_namec = silc_channel_name_check(channel_name, strlen(channel_name),
1529 SILC_STRING_UTF8, 256, NULL);
1530 if (!channel_namec) {
1531 silc_free(channel->channel_name);
1532 silc_hash_table_free(channel->user_list);
1537 silc_mutex_lock(conn->internal->lock);
1539 /* Add channel to cache, the normalized channel name is saved to cache */
1540 if (!silc_idcache_add(conn->internal->channel_cache, channel_namec,
1541 &channel->id, channel)) {
1542 silc_free(channel_namec);
1543 silc_free(channel->channel_name);
1544 silc_hash_table_free(channel->user_list);
1546 silc_mutex_unlock(conn->internal->lock);
1550 silc_mutex_unlock(conn->internal->lock);
1551 silc_client_ref_channel(client, conn, channel);
1553 SILC_LOG_DEBUG(("Added %p", channel));
1558 /* Removes channel from the cache by the channel entry. */
1560 SilcBool silc_client_del_channel(SilcClient client, SilcClientConnection conn,
1561 SilcChannelEntry channel)
1563 SilcIDCacheEntry id_cache;
1571 if (silc_atomic_sub_int16(&channel->internal.refcnt, 1) > 0)
1574 SILC_LOG_DEBUG(("Deleting channel %p", channel));
1576 silc_mutex_lock(conn->internal->lock);
1577 if (silc_idcache_find_by_context(conn->internal->channel_cache, channel,
1579 silc_free(id_cache->name);
1580 ret = silc_idcache_del_by_context(conn->internal->channel_cache,
1582 silc_mutex_unlock(conn->internal->lock);
1587 silc_client_empty_channel(client, conn, channel);
1588 silc_hash_table_free(channel->user_list);
1589 silc_free(channel->channel_name);
1590 silc_free(channel->topic);
1591 if (channel->founder_key)
1592 silc_pkcs_public_key_free(channel->founder_key);
1593 if (channel->internal.send_key)
1594 silc_cipher_free(channel->internal.send_key);
1595 if (channel->internal.receive_key)
1596 silc_cipher_free(channel->internal.receive_key);
1597 if (channel->internal.hmac)
1598 silc_hmac_free(channel->internal.hmac);
1599 if (channel->internal.old_channel_keys) {
1600 silc_dlist_start(channel->internal.old_channel_keys);
1601 while ((key = silc_dlist_get(channel->internal.old_channel_keys)))
1602 silc_cipher_free(key);
1603 silc_dlist_uninit(channel->internal.old_channel_keys);
1605 if (channel->internal.old_hmacs) {
1606 silc_dlist_start(channel->internal.old_hmacs);
1607 while ((hmac = silc_dlist_get(channel->internal.old_hmacs)))
1608 silc_hmac_free(hmac);
1609 silc_dlist_uninit(channel->internal.old_hmacs);
1611 if (channel->channel_pubkeys)
1612 silc_argument_list_free(channel->channel_pubkeys,
1613 SILC_ARGUMENT_PUBLIC_KEY);
1614 silc_client_del_channel_private_keys(client, conn, channel);
1615 silc_atomic_uninit16(&channel->internal.refcnt);
1616 silc_rwlock_free(channel->internal.lock);
1617 silc_schedule_task_del_by_context(conn->client->schedule, channel);
1623 /* Replaces the channel ID of the `channel' to `new_id'. Returns FALSE
1624 if the ID could not be changed. This handles entry locking internally. */
1626 SilcBool silc_client_replace_channel_id(SilcClient client,
1627 SilcClientConnection conn,
1628 SilcChannelEntry channel,
1629 SilcChannelID *new_id)
1631 SilcBool ret = FALSE;
1636 SILC_LOG_DEBUG(("Old Channel ID id(%s)",
1637 silc_id_render(&channel->id, SILC_ID_CHANNEL)));
1638 SILC_LOG_DEBUG(("New Channel ID id(%s)",
1639 silc_id_render(new_id, SILC_ID_CHANNEL)));
1642 silc_rwlock_wrlock(channel->internal.lock);
1643 silc_mutex_lock(conn->internal->lock);
1644 silc_idcache_update_by_context(conn->internal->channel_cache, channel,
1645 new_id, NULL, FALSE);
1646 silc_mutex_unlock(conn->internal->lock);
1647 silc_rwlock_unlock(channel->internal.lock);
1654 void silc_client_lock_channel(SilcChannelEntry channel_entry)
1656 silc_rwlock_rdlock(channel_entry->internal.lock);
1659 /* Unlock channel */
1661 void silc_client_unlock_channel(SilcChannelEntry channel_entry)
1663 silc_rwlock_unlock(channel_entry->internal.lock);
1666 /* Take reference of channel entry */
1668 SilcChannelEntry silc_client_ref_channel(SilcClient client,
1669 SilcClientConnection conn,
1670 SilcChannelEntry channel_entry)
1672 silc_atomic_add_int16(&channel_entry->internal.refcnt, 1);
1673 SILC_LOG_DEBUG(("Channel %p refcnt %d->%d", channel_entry,
1674 silc_atomic_get_int16(&channel_entry->internal.refcnt) - 1,
1675 silc_atomic_get_int16(&channel_entry->internal.refcnt)));
1676 return channel_entry;
1679 /* Release reference of channel entry */
1681 void silc_client_unref_channel(SilcClient client, SilcClientConnection conn,
1682 SilcChannelEntry channel_entry)
1684 if (channel_entry) {
1685 SILC_LOG_DEBUG(("Channel %p refcnt %d->%d", channel_entry,
1686 silc_atomic_get_int16(&channel_entry->internal.refcnt),
1687 silc_atomic_get_int16(&channel_entry->internal.refcnt)
1689 silc_client_del_channel(client, conn, channel_entry);
1693 /* Free channel entry list */
1695 void silc_client_list_free_channels(SilcClient client,
1696 SilcClientConnection conn,
1697 SilcDList channel_list)
1699 SilcChannelEntry channel_entry;
1702 silc_dlist_start(channel_list);
1703 while ((channel_entry = silc_dlist_get(channel_list)))
1704 silc_client_unref_channel(client, conn, channel_entry);
1706 silc_dlist_uninit(channel_list);
1710 /************************* Server Searching Locally *************************/
1712 /* Finds entry for server by the server name. */
1714 SilcServerEntry silc_client_get_server(SilcClient client,
1715 SilcClientConnection conn,
1718 SilcIDCacheEntry id_cache;
1719 SilcServerEntry entry;
1721 if (!client || !conn || !server_name)
1724 SILC_LOG_DEBUG(("Find server by name %s", server_name));
1726 /* Normalize server name for search */
1727 server_name = silc_identifier_check(server_name, strlen(server_name),
1728 SILC_STRING_UTF8, 256, NULL);
1732 silc_mutex_lock(conn->internal->lock);
1734 if (!silc_idcache_find_by_name_one(conn->internal->server_cache,
1735 server_name, &id_cache)) {
1736 silc_free(server_name);
1737 silc_mutex_unlock(conn->internal->lock);
1741 SILC_LOG_DEBUG(("Found"));
1744 entry = id_cache->context;
1745 silc_client_ref_server(client, conn, entry);
1747 silc_mutex_unlock(conn->internal->lock);
1749 silc_free(server_name);
1754 /* Finds entry for server by the server ID. */
1756 SilcServerEntry silc_client_get_server_by_id(SilcClient client,
1757 SilcClientConnection conn,
1758 SilcServerID *server_id)
1760 SilcIDCacheEntry id_cache;
1761 SilcServerEntry entry;
1763 if (!client || !conn || !server_id)
1766 SILC_LOG_DEBUG(("Find server by id %s",
1767 silc_id_render(server_id, SILC_ID_SERVER)));
1769 silc_mutex_lock(conn->internal->lock);
1771 if (!silc_idcache_find_by_id_one(conn->internal->server_cache,
1772 server_id, &id_cache)) {
1773 silc_mutex_unlock(conn->internal->lock);
1777 SILC_LOG_DEBUG(("Found"));
1780 entry = id_cache->context;
1781 silc_client_ref_server(client, conn, entry);
1783 silc_mutex_unlock(conn->internal->lock);
1788 /*********************** Server Resolving from Server ***********************/
1790 /* Resolving context */
1793 SilcGetServerCallback completion;
1795 } *SilcClientGetServerInternal;
1797 /* Resolving command callback */
1799 static SilcBool silc_client_get_server_cb(SilcClient client,
1800 SilcClientConnection conn,
1801 SilcCommand command,
1807 SilcClientGetServerInternal i = context;
1808 SilcServerEntry server;
1810 if (error != SILC_STATUS_OK) {
1811 SILC_LOG_DEBUG(("Resolving failed: %s", silc_get_status_message(error)));
1813 i->completion(client, conn, error, NULL, i->context);
1817 /* Add the returned servers to list */
1818 if (i->completion) {
1819 server = va_arg(ap, SilcServerEntry);
1820 silc_client_ref_server(client, conn, server);
1821 silc_dlist_add(i->servers, server);
1822 server->internal.resolve_cmd_ident = 0;
1825 if (status == SILC_STATUS_OK || status == SILC_STATUS_LIST_END) {
1826 /* Deliver the servers to the caller */
1827 if (i->completion) {
1828 SILC_LOG_DEBUG(("Resolved %d servers", silc_dlist_count(i->servers)));
1829 silc_dlist_start(i->servers);
1830 i->completion(client, conn, SILC_STATUS_OK, i->servers, i->context);
1838 silc_client_list_free_servers(client, conn, i->servers);
1843 /* Resolve server by server ID */
1846 silc_client_get_server_by_id_resolve(SilcClient client,
1847 SilcClientConnection conn,
1848 SilcServerID *server_id,
1849 SilcGetServerCallback completion,
1852 SilcClientGetServerInternal i;
1853 SilcServerEntry server;
1855 SilcUInt16 cmd_ident;
1857 if (!client || !conn || !server_id || !completion)
1860 SILC_LOG_DEBUG(("Resolve server by id %s",
1861 silc_id_render(server_id, SILC_ID_SERVER)));
1863 i = silc_calloc(1, sizeof(*i));
1866 i->completion = completion;
1867 i->context = context;
1868 i->servers = silc_dlist_init();
1874 /* Attach to resolving, if on going */
1875 server = silc_client_get_server_by_id(client, conn, server_id);
1876 if (server && server->internal.resolve_cmd_ident) {
1877 SILC_LOG_DEBUG(("Attach to existing resolving"));
1878 silc_client_unref_server(client, conn, server);
1879 silc_client_command_pending(conn, SILC_COMMAND_NONE,
1880 server->internal.resolve_cmd_ident,
1881 silc_client_get_server_cb, i);
1882 return server->internal.resolve_cmd_ident;
1885 /* Send the command */
1886 idp = silc_id_payload_encode(server_id, SILC_ID_SERVER);
1887 cmd_ident = silc_client_command_send(client, conn, SILC_COMMAND_IDENTIFY,
1888 silc_client_get_server_cb, i, 1,
1889 5, silc_buffer_datalen(idp));
1890 silc_buffer_free(idp);
1891 if (!cmd_ident && completion)
1892 completion(client, conn, SILC_STATUS_ERR_RESOURCE_LIMIT, NULL, context);
1894 if (server && cmd_ident)
1895 server->internal.resolve_cmd_ident = cmd_ident;
1897 silc_client_unref_server(client, conn, server);
1902 /************************** Server Entry Routines ***************************/
1904 /* Add new server entry */
1906 SilcServerEntry silc_client_add_server(SilcClient client,
1907 SilcClientConnection conn,
1908 const char *server_name,
1909 const char *server_info,
1910 SilcServerID *server_id)
1912 SilcServerEntry server_entry;
1913 char *server_namec = NULL;
1918 SILC_LOG_DEBUG(("Adding new server %s", server_name));
1920 server_entry = silc_calloc(1, sizeof(*server_entry));
1924 silc_rwlock_alloc(&server_entry->internal.lock);
1925 silc_atomic_init8(&server_entry->internal.refcnt, 0);
1926 server_entry->id = *server_id;
1928 server_entry->server_name = strdup(server_name);
1930 server_entry->server_info = strdup(server_info);
1932 /* Normalize server name */
1934 server_namec = silc_identifier_check(server_name, strlen(server_name),
1935 SILC_STRING_UTF8, 256, NULL);
1936 if (!server_namec) {
1937 silc_free(server_entry->server_name);
1938 silc_free(server_entry->server_info);
1939 silc_free(server_entry);
1944 silc_mutex_lock(conn->internal->lock);
1946 /* Add server to cache */
1947 if (!silc_idcache_add(conn->internal->server_cache, server_namec,
1948 &server_entry->id, server_entry)) {
1949 silc_free(server_namec);
1950 silc_free(server_entry->server_name);
1951 silc_free(server_entry->server_info);
1952 silc_free(server_entry);
1953 silc_mutex_unlock(conn->internal->lock);
1957 silc_mutex_unlock(conn->internal->lock);
1958 silc_client_ref_server(client, conn, server_entry);
1960 SILC_LOG_DEBUG(("Added %p", server_entry));
1962 return server_entry;
1965 /* Removes server from the cache by the server entry. */
1967 SilcBool silc_client_del_server(SilcClient client, SilcClientConnection conn,
1968 SilcServerEntry server)
1970 SilcIDCacheEntry id_cache;
1976 if (silc_atomic_sub_int8(&server->internal.refcnt, 1) > 0)
1979 SILC_LOG_DEBUG(("Deleting server %p", server));
1981 silc_mutex_lock(conn->internal->lock);
1982 if (silc_idcache_find_by_context(conn->internal->server_cache, server,
1984 silc_free(id_cache->name);
1985 ret = silc_idcache_del_by_context(conn->internal->server_cache,
1987 silc_mutex_unlock(conn->internal->lock);
1989 silc_free(server->server_name);
1990 silc_free(server->server_info);
1991 if (server->public_key)
1992 silc_pkcs_public_key_free(server->public_key);
1993 silc_atomic_uninit8(&server->internal.refcnt);
1994 silc_rwlock_free(server->internal.lock);
2000 /* Updates the `server_entry' with the new information sent as argument. */
2002 void silc_client_update_server(SilcClient client,
2003 SilcClientConnection conn,
2004 SilcServerEntry server_entry,
2005 const char *server_name,
2006 const char *server_info)
2008 char *server_namec = NULL;
2010 SILC_LOG_DEBUG(("Updating server %p", server_entry));
2013 (!server_entry->server_name ||
2014 !silc_utf8_strcasecmp(server_entry->server_name, server_name))) {
2016 server_namec = silc_identifier_check(server_name, strlen(server_name),
2017 SILC_STRING_UTF8, 256, NULL);
2021 silc_free(server_entry->server_name);
2022 server_entry->server_name = strdup(server_name);
2023 if (!server_entry->server_name)
2026 /* Update cache entry */
2027 silc_mutex_lock(conn->internal->lock);
2028 silc_idcache_update_by_context(conn->internal->server_cache, server_entry,
2029 NULL, server_namec, TRUE);
2030 silc_mutex_unlock(conn->internal->lock);
2034 (!server_entry->server_info ||
2035 !silc_utf8_strcasecmp(server_entry->server_info, server_info))) {
2036 silc_free(server_entry->server_info);
2037 server_entry->server_info = strdup(server_info);
2043 void silc_client_lock_server(SilcServerEntry server_entry)
2045 silc_rwlock_rdlock(server_entry->internal.lock);
2050 void silc_client_unlock_server(SilcServerEntry server_entry)
2052 silc_rwlock_unlock(server_entry->internal.lock);
2055 /* Take reference of server entry */
2057 SilcServerEntry silc_client_ref_server(SilcClient client,
2058 SilcClientConnection conn,
2059 SilcServerEntry server_entry)
2061 silc_atomic_add_int8(&server_entry->internal.refcnt, 1);
2062 SILC_LOG_DEBUG(("Server %p refcnt %d->%d", server_entry,
2063 silc_atomic_get_int8(&server_entry->internal.refcnt) - 1,
2064 silc_atomic_get_int8(&server_entry->internal.refcnt)));
2065 return server_entry;
2068 /* Release reference of server entry */
2070 void silc_client_unref_server(SilcClient client, SilcClientConnection conn,
2071 SilcServerEntry server_entry)
2074 SILC_LOG_DEBUG(("Server %p refcnt %d->%d", server_entry,
2075 silc_atomic_get_int8(&server_entry->internal.refcnt),
2076 silc_atomic_get_int8(&server_entry->internal.refcnt)
2078 silc_client_del_server(client, conn, server_entry);
2082 /* Free server entry list */
2084 void silc_client_list_free_servers(SilcClient client,
2085 SilcClientConnection conn,
2086 SilcDList server_list)
2088 SilcServerEntry server_entry;
2091 silc_dlist_start(server_list);
2092 while ((server_entry = silc_dlist_get(server_list)))
2093 silc_client_unref_server(client, conn, server_entry);
2095 silc_dlist_uninit(server_list);