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(SilcClient client,
66 SilcClientConnection conn,
70 SilcIDCacheEntry id_cache;
73 SilcClientEntry entry;
76 if (!client || !conn || !nickname)
79 SILC_LOG_DEBUG(("Find clients by nickname %s", nickname));
81 /* Normalize nickname for search */
82 nicknamec = silc_identifier_check(nickname, strlen(nickname),
83 SILC_STRING_UTF8, 128, NULL);
87 clients = silc_dlist_init();
93 silc_mutex_lock(conn->internal->lock);
96 silc_list_init(list, struct SilcIDCacheEntryStruct, next);
97 if (!silc_idcache_find_by_name(conn->internal->client_cache, nicknamec,
99 silc_mutex_unlock(conn->internal->lock);
100 silc_free(nicknamec);
101 silc_dlist_uninit(clients);
106 /* Take all without any further checking */
107 silc_list_start(list);
108 while ((id_cache = silc_list_get(list))) {
109 entry = id_cache->context;
110 if (entry->internal.valid) {
111 silc_client_ref_client(client, conn, id_cache->context);
112 silc_dlist_add(clients, id_cache->context);
116 /* Check multiple cache entries for exact match */
117 silc_list_start(list);
118 while ((id_cache = silc_list_get(list))) {
119 entry = id_cache->context;
120 if (silc_utf8_strcasecmp(entry->nickname, format) &&
121 entry->internal.valid) {
122 silc_client_ref_client(client, conn, entry);
123 silc_dlist_add(clients, entry);
128 silc_mutex_unlock(conn->internal->lock);
130 silc_dlist_start(clients);
132 silc_free(nicknamec);
136 /********************** Client Resolving from Server ************************/
138 /* Resolving context */
141 SilcGetClientCallback completion;
143 SilcClientEntry client_entry;
144 } *SilcClientGetClientInternal;
146 /* Resolving command callback */
148 static SilcBool silc_client_get_clients_cb(SilcClient client,
149 SilcClientConnection conn,
156 SilcClientGetClientInternal i = context;
157 SilcClientEntry client_entry;
159 if (error != SILC_STATUS_OK) {
160 SILC_LOG_DEBUG(("Resolving failed: %s", silc_get_status_message(error)));
162 if (i->client_entry) {
163 i->client_entry->internal.resolve_cmd_ident = 0;
164 silc_client_unref_client(client, conn, i->client_entry);
168 i->completion(client, conn, error, NULL, i->context);
172 /* Add the returned client to list */
174 client_entry = va_arg(ap, SilcClientEntry);
175 silc_client_ref_client(client, conn, client_entry);
176 silc_dlist_add(i->clients, client_entry);
177 client_entry->internal.resolve_cmd_ident = 0;
180 if (status == SILC_STATUS_OK || status == SILC_STATUS_LIST_END) {
181 /* Deliver the clients to the caller */
183 SILC_LOG_DEBUG(("Resolved %d clients", silc_dlist_count(i->clients)));
185 if (i->client_entry) {
186 i->client_entry->internal.resolve_cmd_ident = 0;
187 silc_client_unref_client(client, conn, i->client_entry);
190 silc_dlist_start(i->clients);
191 i->completion(client, conn, SILC_STATUS_OK, i->clients, i->context);
199 silc_client_list_free(client, conn, i->clients);
204 /* Resolves client information from server by the client ID. */
207 silc_client_get_client_by_id_resolve(SilcClient client,
208 SilcClientConnection conn,
209 SilcClientID *client_id,
210 SilcBuffer attributes,
211 SilcGetClientCallback completion,
214 SilcClientGetClientInternal i;
215 SilcClientEntry client_entry;
217 SilcUInt16 cmd_ident;
219 if (!client || !conn | !client_id)
222 SILC_LOG_DEBUG(("Resolve client by ID (%s)",
223 silc_id_render(client_id, SILC_ID_CLIENT)));
225 i = silc_calloc(1, sizeof(*i));
228 i->completion = completion;
229 i->context = context;
230 i->clients = silc_dlist_init();
236 /* Attach to resolving, if on going */
237 client_entry = silc_client_get_client_by_id(client, conn, client_id);
238 if (client_entry && client_entry->internal.resolve_cmd_ident) {
239 SILC_LOG_DEBUG(("Attach to existing resolving"));
240 silc_client_unref_client(client, conn, client_entry);
241 silc_client_command_pending(conn, SILC_COMMAND_NONE,
242 client_entry->internal.resolve_cmd_ident,
243 silc_client_get_clients_cb, i);
244 return client_entry->internal.resolve_cmd_ident;
247 /* Send the command */
248 idp = silc_id_payload_encode(client_id, SILC_ID_CLIENT);
249 cmd_ident = silc_client_command_send(client, conn, SILC_COMMAND_WHOIS,
250 silc_client_get_clients_cb, i,
251 2, 3, silc_buffer_datalen(attributes),
252 4, silc_buffer_datalen(idp));
253 if (!cmd_ident && completion)
254 completion(client, conn, SILC_STATUS_ERR_RESOURCE_LIMIT, NULL, context);
256 if (client_entry && cmd_ident) {
257 client_entry->internal.resolve_cmd_ident = cmd_ident;
258 i->client_entry = client_entry;
260 silc_client_unref_client(client, conn, client_entry);
263 silc_buffer_free(idp);
268 /* Finds client entry or entries by the `nickname' and `server'. The
269 completion callback will be called when the client entries has been
270 found. Used internally by the library. */
272 static SilcUInt16 silc_client_get_clients_i(SilcClient client,
273 SilcClientConnection conn,
275 const char *nickname,
277 SilcBuffer attributes,
278 SilcGetClientCallback completion,
281 SilcClientGetClientInternal i;
282 char userhost[768 + 1];
285 SILC_LOG_DEBUG(("Resolve client by %s command",
286 silc_get_command_name(command)));
288 if (!client || !conn)
290 if (!nickname && !attributes)
293 i = silc_calloc(1, sizeof(*i));
296 i->clients = silc_dlist_init();
301 i->completion = completion;
302 i->context = context;
304 memset(userhost, 0, sizeof(userhost));
305 if (nickname && server) {
306 len = strlen(nickname) + strlen(server) + 3;
307 silc_strncat(userhost, len, nickname, strlen(nickname));
308 silc_strncat(userhost, len, "@", 1);
309 silc_strncat(userhost, len, server, strlen(server));
310 } else if (nickname) {
311 silc_strncat(userhost, sizeof(userhost) - 1, nickname, strlen(nickname));
314 /* Send the command */
315 if (command == SILC_COMMAND_IDENTIFY)
316 return silc_client_command_send(client, conn, SILC_COMMAND_IDENTIFY,
317 silc_client_get_clients_cb, i,
318 1, 1, userhost, strlen(userhost));
319 return silc_client_command_send(client, conn, SILC_COMMAND_WHOIS,
320 silc_client_get_clients_cb, i,
321 2, 1, userhost, strlen(userhost),
322 3, silc_buffer_datalen(attributes));
325 /* Get clients from server with IDENTIFY command */
327 SilcUInt16 silc_client_get_clients(SilcClient client,
328 SilcClientConnection conn,
329 const char *nickname,
331 SilcGetClientCallback completion,
334 return silc_client_get_clients_i(client, conn, SILC_COMMAND_IDENTIFY,
335 nickname, server, NULL,
336 completion, context);
339 /* Get clients from server with WHOIS command */
341 SilcUInt16 silc_client_get_clients_whois(SilcClient client,
342 SilcClientConnection conn,
343 const char *nickname,
345 SilcBuffer attributes,
346 SilcGetClientCallback completion,
349 return silc_client_get_clients_i(client, conn, SILC_COMMAND_WHOIS,
350 nickname, server, attributes,
351 completion, context);
354 /* ID list resolving context */
356 SilcGetClientCallback completion;
358 SilcBuffer client_id_list;
359 SilcUInt32 list_count;
360 } *GetClientsByListInternal;
362 static SilcBool silc_client_get_clients_list_cb(SilcClient client,
363 SilcClientConnection conn,
370 GetClientsByListInternal i = context;
371 SilcClientEntry client_entry;
377 /* Process the list after all replies have been received */
378 if (status != SILC_STATUS_OK && !SILC_STATUS_IS_ERROR(status) &&
379 status != SILC_STATUS_LIST_END)
382 SILC_LOG_DEBUG(("Resolved all clients"));
384 clients = silc_dlist_init();
386 status = SILC_STATUS_ERR_RESOURCE_LIMIT;
390 for (c = 0; c < i->list_count; c++) {
392 SILC_GET16_MSB(idp_len, i->client_id_list->data + 2);
394 if (!silc_id_payload_parse_id(i->client_id_list->data, idp_len, &id)) {
395 status = SILC_STATUS_ERR_BAD_CLIENT_ID;
399 /* Get client entry */
400 client_entry = silc_client_get_client_by_id(client, conn, &id.u.client_id);
402 silc_dlist_add(clients, client_entry);
404 if (!silc_buffer_pull(i->client_id_list, idp_len)) {
405 status = SILC_STATUS_ERR_BAD_CLIENT_ID;
410 silc_dlist_start(clients);
411 status = SILC_STATUS_OK;
413 i->completion(client, conn, status, clients, i->context);
416 if (status != SILC_STATUS_OK && i->completion)
417 i->completion(client, conn, status, NULL, i->context);
419 silc_client_list_free(client, conn, clients);
420 silc_buffer_free(i->client_id_list);
426 /* Gets client entries by the list of client ID's `client_id_list'. This
427 always resolves those client ID's it does not know yet from the server
428 so this function might take a while. The `client_id_list' is a list
429 of ID Payloads added one after other. JOIN command reply and USERS
430 command reply for example returns this sort of list. The `completion'
431 will be called after the entries are available. */
433 SilcUInt16 silc_client_get_clients_by_list(SilcClient client,
434 SilcClientConnection conn,
435 SilcUInt32 list_count,
436 SilcBuffer client_id_list,
437 SilcGetClientCallback completion,
440 GetClientsByListInternal in;
441 SilcClientEntry entry;
442 unsigned char **res_argv = NULL;
443 SilcUInt32 *res_argv_lens = NULL, *res_argv_types = NULL, res_argc = 0;
444 SilcUInt16 idp_len, cmd_ident;
449 SILC_LOG_DEBUG(("Resolve clients from Client ID list"));
451 if (!client || !conn || !client_id_list)
454 in = silc_calloc(1, sizeof(*in));
457 in->completion = completion;
458 in->context = context;
459 in->list_count = list_count;
460 in->client_id_list = silc_buffer_copy(client_id_list);
461 if (!in->client_id_list)
464 for (i = 0; i < list_count; i++) {
466 SILC_GET16_MSB(idp_len, client_id_list->data + 2);
468 if (!silc_id_payload_parse_id(client_id_list->data, idp_len, &id))
471 /* Check if we have this client cached already. If we don't have the
472 entry or it has incomplete info, then resolve it from the server. */
473 entry = silc_client_get_client_by_id(client, conn, &id.u.client_id);
474 if (!entry || !entry->nickname[0] || !entry->username[0] ||
477 res_argv = silc_calloc(list_count, sizeof(*res_argv));
478 res_argv_lens = silc_calloc(list_count, sizeof(*res_argv_lens));
479 res_argv_types = silc_calloc(list_count, sizeof(*res_argv_types));
480 if (!res_argv || !res_argv_lens || !res_argv_types) {
481 silc_client_unref_client(client, conn, entry);
486 res_argv[res_argc] = client_id_list->data;
487 res_argv_lens[res_argc] = idp_len;
488 res_argv_types[res_argc] = res_argc + 4;
491 silc_client_unref_client(client, conn, entry);
493 if (!silc_buffer_pull(client_id_list, idp_len))
496 silc_buffer_start(client_id_list);
498 /* Query the unknown client information from server */
500 cmd_ident = silc_client_command_send_argv(client,
501 conn, SILC_COMMAND_WHOIS,
502 silc_client_get_clients_list_cb,
503 in, res_argc, res_argv,
507 silc_free(res_argv_lens);
508 silc_free(res_argv_types);
512 /* We have the clients in cache, get them and call the completion */
513 silc_client_get_clients_list_cb(client, conn, SILC_COMMAND_WHOIS,
514 SILC_STATUS_OK, SILC_STATUS_OK, in, tmp);
518 silc_buffer_free(in->client_id_list);
521 silc_free(res_argv_lens);
522 silc_free(res_argv_types);
529 SilcClientConnection conn;
530 SilcChannelID channel_id;
531 SilcGetClientCallback completion;
534 } *GetClientsByChannelInternal;
536 SILC_CLIENT_CMD_FUNC(get_clients_by_channel_cb)
538 GetClientsByChannelInternal i = context;
539 SilcClientEntry *clients = NULL;
540 SilcUInt32 clients_count = 0;
541 SilcBool found = FALSE;
542 SilcChannelEntry channel;
543 SilcHashTableList htl;
552 channel = silc_client_get_channel_by_id(i->client, i->conn, &i->channel_id);
553 if (channel && !silc_hash_table_count(channel->user_list)) {
554 clients = silc_calloc(silc_hash_table_count(channel->user_list),
556 silc_hash_table_list(channel->user_list, &htl);
557 while (silc_hash_table_get(&htl, NULL, (void *)&chu))
558 clients[clients_count++] = chu->client;
559 silc_hash_table_list_reset(&htl);
564 i->completion(i->client, i->conn, clients, clients_count, i->context);
567 i->completion(i->client, i->conn, NULL, 0, i->context);
573 /* Gets client entries by the channel entry indicated by `channel'. Thus,
574 it resolves the clients currently on that channel. */
576 void silc_client_get_clients_by_channel(SilcClient client,
577 SilcClientConnection conn,
578 SilcChannelEntry channel,
579 SilcGetClientCallback completion,
582 GetClientsByChannelInternal in;
583 SilcHashTableList htl;
585 SilcClientEntry entry;
586 unsigned char **res_argv = NULL;
587 SilcUInt32 *res_argv_lens = NULL, *res_argv_types = NULL, res_argc = 0;
589 SilcBool wait_res = FALSE;
591 assert(client && conn && channel);
593 SILC_LOG_DEBUG(("Start"));
595 in = silc_calloc(1, sizeof(*in));
598 in->channel_id = *channel->id;
599 in->completion = completion;
600 in->context = context;
602 /* If user list does not exist, send USERS command. */
603 if (!channel->user_list || !silc_hash_table_count(channel->user_list)) {
604 SILC_LOG_DEBUG(("Sending USERS"));
605 silc_client_command_register(client, SILC_COMMAND_USERS, NULL, NULL,
606 silc_client_command_reply_users_i, 0,
608 silc_client_command_send(client, conn, SILC_COMMAND_USERS,
609 conn->cmd_ident, 1, 2, channel->channel_name,
610 strlen(channel->channel_name));
611 silc_client_command_pending(conn, SILC_COMMAND_USERS, conn->cmd_ident,
612 silc_client_command_get_clients_by_channel_cb,
617 silc_hash_table_list(channel->user_list, &htl);
618 while (silc_hash_table_get(&htl, NULL, (void *)&chu)) {
621 /* If the entry has incomplete info, then resolve it from the server. */
622 if (!entry->nickname[0] || !entry->realname) {
623 if (entry->status & SILC_CLIENT_STATUS_RESOLVING) {
624 /* Attach to this resolving and wait until it finishes */
625 silc_client_command_pending(
626 conn, SILC_COMMAND_NONE,
627 entry->resolve_cmd_ident,
628 silc_client_command_get_clients_by_channel_cb,
634 entry->status |= SILC_CLIENT_STATUS_RESOLVING;
635 entry->resolve_cmd_ident = conn->cmd_ident + 1;
637 idp = silc_id_payload_encode(entry->id, SILC_ID_CLIENT);
639 /* No we don't have it, query it from the server. Assemble argument
640 table that will be sent for the WHOIS command later. */
641 res_argv = silc_realloc(res_argv, sizeof(*res_argv) *
643 res_argv_lens = silc_realloc(res_argv_lens, sizeof(*res_argv_lens) *
645 res_argv_types = silc_realloc(res_argv_types, sizeof(*res_argv_types) *
647 res_argv[res_argc] = silc_memdup(idp->data, idp->len);
648 res_argv_lens[res_argc] = idp->len;
649 res_argv_types[res_argc] = res_argc + 4;
652 silc_buffer_free(idp);
655 silc_hash_table_list_reset(&htl);
657 /* Query the client information from server if the list included clients
658 that we don't know about. */
662 /* Send the WHOIS command to server */
663 res_cmd = silc_command_payload_encode(SILC_COMMAND_WHOIS,
664 res_argc, res_argv, res_argv_lens,
665 res_argv_types, ++conn->cmd_ident);
666 silc_client_packet_send(client, conn->sock, SILC_PACKET_COMMAND,
667 NULL, 0, NULL, NULL, res_cmd->data, res_cmd->len,
670 /* Register our own command reply for this command */
671 silc_client_command_register(client, SILC_COMMAND_WHOIS, NULL, NULL,
672 silc_client_command_reply_whois_i, 0,
675 /* Process the applications request after reply has been received */
676 silc_client_command_pending(
677 conn, SILC_COMMAND_WHOIS, conn->cmd_ident,
678 silc_client_command_get_clients_by_channel_cb,
682 silc_buffer_free(res_cmd);
684 silc_free(res_argv_lens);
685 silc_free(res_argv_types);
692 /* We have the clients in cache, get them and call the completion */
693 silc_client_command_get_clients_by_channel_cb((void *)in, NULL);
698 /************************** Client Entry Routines ***************************/
700 /* Creates new client entry and adds it to the ID cache. Returns pointer
703 SilcClientEntry silc_client_add_client(SilcClient client,
704 SilcClientConnection conn,
705 char *nickname, char *username,
706 char *userinfo, SilcClientID *id,
709 SilcClientEntry client_entry;
712 SILC_LOG_DEBUG(("Adding new client entry"));
714 /* Save the client infos */
715 client_entry = silc_calloc(1, sizeof(*client_entry));
719 silc_rwlock_alloc(&client_entry->internal.lock);
720 silc_atomic_init8(&client_entry->internal.refcnt, 0);
721 client_entry->id = *id;
722 client_entry->mode = mode;
723 client_entry->realname = userinfo ? strdup(userinfo) : NULL;
724 silc_parse_userfqdn(nickname, client_entry->nickname,
725 sizeof(client_entry->nickname),
726 client_entry->server,
727 sizeof(client_entry->server));
728 silc_parse_userfqdn(username, client_entry->username,
729 sizeof(client_entry->username),
730 client_entry->hostname,
731 sizeof(client_entry->hostname));
732 client_entry->channels = silc_hash_table_alloc(1, silc_hash_ptr, NULL, NULL,
733 NULL, NULL, NULL, TRUE);
734 if (!client_entry->channels) {
735 silc_free(client_entry->realname);
736 silc_free(client_entry);
740 /* Normalize nickname */
741 if (client_entry->nickname[0]) {
742 nick = silc_identifier_check(client_entry->nickname,
743 strlen(client_entry->nickname),
744 SILC_STRING_UTF8, 128, NULL);
746 silc_free(client_entry->realname);
747 silc_hash_table_free(client_entry->channels);
748 silc_free(client_entry);
753 silc_mutex_lock(conn->internal->lock);
755 /* Add client to cache, the normalized nickname is saved to cache */
756 if (!silc_idcache_add(conn->internal->client_cache, nick,
757 &client_entry->id, client_entry)) {
759 silc_free(client_entry->realname);
760 silc_hash_table_free(client_entry->channels);
761 silc_free(client_entry);
762 silc_mutex_unlock(conn->internal->lock);
766 client_entry->nickname_normalized = nick;
768 silc_mutex_unlock(conn->internal->lock);
769 silc_client_ref_client(client, conn, client_entry);
771 /* Format the nickname */
772 silc_client_nickname_format(client, conn, client_entry, FALSE);
774 if (client_entry->nickname[0])
775 client_entry->internal.valid = TRUE;
777 SILC_LOG_DEBUG(("Added %p", client_entry));
782 /* Updates the `client_entry' with the new information sent as argument.
783 This handles entry locking internally. */
785 void silc_client_update_client(SilcClient client,
786 SilcClientConnection conn,
787 SilcClientEntry client_entry,
788 const char *nickname,
789 const char *username,
790 const char *userinfo,
795 SILC_LOG_DEBUG(("Update client entry"));
797 silc_rwlock_wrlock(client_entry->internal.lock);
799 if (!client_entry->realname && userinfo)
800 client_entry->realname = strdup(userinfo);
801 if ((!client_entry->username[0] || !client_entry->hostname[0]) && username)
802 silc_parse_userfqdn(username, client_entry->username,
803 sizeof(client_entry->username),
804 client_entry->hostname,
805 sizeof(client_entry->username));
806 if (!client_entry->nickname[0] && nickname) {
807 silc_parse_userfqdn(nickname, client_entry->nickname,
808 sizeof(client_entry->nickname),
809 client_entry->server,
810 sizeof(client_entry->server));
812 /* Normalize nickname */
813 nick = silc_identifier_check(client_entry->nickname,
814 strlen(client_entry->nickname),
815 SILC_STRING_UTF8, 128, NULL);
817 silc_rwlock_unlock(client_entry->internal.lock);
821 /* Format nickname */
822 silc_client_nickname_format(client, conn, client_entry,
823 client_entry == conn->local_entry);
825 /* Update cache entry */
826 silc_mutex_lock(conn->internal->lock);
827 silc_idcache_update_by_context(conn->internal->client_cache,
828 client_entry, NULL, nick, TRUE);
829 silc_mutex_unlock(conn->internal->lock);
830 client_entry->nickname_normalized = nick;
831 client_entry->internal.valid = TRUE;
833 client_entry->mode = mode;
835 silc_rwlock_unlock(client_entry->internal.lock);
838 /* Change a client's nickname. Must be called with `client_entry' locked. */
840 SilcBool silc_client_change_nickname(SilcClient client,
841 SilcClientConnection conn,
842 SilcClientEntry client_entry,
843 const char *new_nick,
844 SilcClientID *new_id,
845 const unsigned char *idp,
850 SILC_LOG_DEBUG(("Change nickname %s to %s", client_entry->nickname,
853 /* Normalize nickname */
854 tmp = silc_identifier_check(new_nick, strlen(new_nick),
855 SILC_STRING_UTF8, 128, NULL);
859 /* Update the client entry */
860 silc_mutex_lock(conn->internal->lock);
861 if (!silc_idcache_update_by_context(conn->internal->client_cache,
862 client_entry, new_id, tmp, TRUE)) {
864 silc_mutex_unlock(conn->internal->lock);
867 silc_mutex_unlock(conn->internal->lock);
869 memset(client_entry->nickname, 0, sizeof(client_entry->nickname));
870 memcpy(client_entry->nickname, new_nick, strlen(new_nick));
871 client_entry->nickname_normalized = tmp;
872 silc_client_nickname_format(client, conn, client_entry,
873 client_entry == conn->local_entry);
875 /* For my client entry, update ID and set new ID to packet stream */
876 if (client_entry == conn->local_entry) {
877 if (idp && idp_len) {
878 silc_buffer_enlarge(conn->internal->local_idp, idp_len);
879 silc_buffer_put(conn->internal->local_idp, idp, idp_len);
882 silc_packet_set_ids(conn->stream, SILC_ID_CLIENT, conn->local_id,
886 client_entry->internal.valid = TRUE;
890 /* Deletes the client entry and frees all memory. */
892 void silc_client_del_client_entry(SilcClient client,
893 SilcClientConnection conn,
894 SilcClientEntry client_entry)
896 silc_free(client_entry->realname);
897 silc_free(client_entry->nickname_normalized);
898 silc_free(client_entry->internal.key);
899 if (client_entry->public_key)
900 silc_pkcs_public_key_free(client_entry->public_key);
901 silc_hash_table_free(client_entry->channels);
902 if (client_entry->internal.send_key)
903 silc_cipher_free(client_entry->internal.send_key);
904 if (client_entry->internal.receive_key)
905 silc_cipher_free(client_entry->internal.receive_key);
906 if (client_entry->internal.hmac_send)
907 silc_hmac_free(client_entry->internal.hmac_send);
908 if (client_entry->internal.hmac_receive)
909 silc_hmac_free(client_entry->internal.hmac_receive);
910 silc_client_ftp_session_free_client(client, client_entry);
911 if (client_entry->internal.ke)
912 silc_client_abort_key_agreement(client, conn, client_entry);
913 silc_atomic_uninit8(&client_entry->internal.refcnt);
914 silc_rwlock_free(client_entry->internal.lock);
915 silc_free(client_entry);
918 /* Removes client from the cache by the client entry. */
920 SilcBool silc_client_del_client(SilcClient client, SilcClientConnection conn,
921 SilcClientEntry client_entry)
928 if (silc_atomic_sub_int8(&client_entry->internal.refcnt, 1) > 0)
931 SILC_LOG_DEBUG(("Deleting client %p", client_entry));
933 silc_mutex_lock(conn->internal->lock);
934 ret = silc_idcache_del_by_context(conn->internal->client_cache,
936 silc_mutex_unlock(conn->internal->lock);
939 /* Remove from channels */
940 silc_client_remove_from_channels(client, conn, client_entry);
942 /* Free the client entry data */
943 silc_client_del_client_entry(client, conn, client_entry);
949 /* Internal routine used to find client by ID and if not found this creates
950 new client entry and returns it. */
952 SilcClientEntry silc_client_get_client(SilcClient client,
953 SilcClientConnection conn,
954 SilcClientID *client_id)
956 SilcClientEntry client_entry;
958 client_entry = silc_client_get_client_by_id(client, conn, client_id);
960 client_entry = silc_client_add_client(client, conn, NULL, NULL, NULL,
964 silc_client_ref_client(client, conn, client_entry);
972 void silc_client_lock_client(SilcClientEntry client_entry)
974 silc_rwlock_rdlock(client_entry->internal.lock);
979 void silc_client_unlock_client(SilcClientEntry client_entry)
981 silc_rwlock_unlock(client_entry->internal.lock);
984 /* Take reference of client entry */
986 SilcClientEntry silc_client_ref_client(SilcClient client,
987 SilcClientConnection conn,
988 SilcClientEntry client_entry)
990 silc_atomic_add_int8(&client_entry->internal.refcnt, 1);
991 SILC_LOG_DEBUG(("Client %p refcnt %d->%d", client_entry,
992 silc_atomic_get_int8(&client_entry->internal.refcnt) - 1,
993 silc_atomic_get_int8(&client_entry->internal.refcnt)));
997 /* Release reference of client entry */
999 void silc_client_unref_client(SilcClient client, SilcClientConnection conn,
1000 SilcClientEntry client_entry)
1003 SILC_LOG_DEBUG(("Client %p refcnt %d->%d", client_entry,
1004 silc_atomic_get_int8(&client_entry->internal.refcnt),
1005 silc_atomic_get_int8(&client_entry->internal.refcnt) - 1));
1006 silc_client_del_client(client, conn, client_entry);
1010 /* Free client entry list */
1012 void silc_client_list_free(SilcClient client, SilcClientConnection conn,
1013 SilcDList client_list)
1015 SilcClientEntry client_entry;
1018 silc_dlist_start(client_list);
1019 while ((client_entry = silc_dlist_get(client_list)))
1020 silc_client_unref_client(client, conn, client_entry);
1022 silc_dlist_uninit(client_list);
1026 /* Formats the nickname of the client specified by the `client_entry'.
1027 If the format is specified by the application this will format the
1028 nickname and replace the old nickname in the client entry. If the
1029 format string is not specified then this function has no effect.
1030 Returns the client entry that was formatted. */
1032 SilcClientEntry silc_client_nickname_format(SilcClient client,
1033 SilcClientConnection conn,
1034 SilcClientEntry client_entry,
1038 char newnick[128 + 1];
1039 int i, off = 0, len;
1042 SilcClientEntry entry, unformatted = NULL;
1044 if (!client->internal->params->nickname_format[0])
1045 return client_entry;
1046 if (!client_entry->nickname[0])
1049 SILC_LOG_DEBUG(("Format nickname"));
1051 /* Get all clients with same nickname. Do not perform the formatting
1052 if there aren't any clients with same nickname unless the application
1053 is forcing us to do so. */
1054 clients = silc_client_get_clients_local(client, conn,
1055 client_entry->nickname, NULL);
1058 if (silc_dlist_count(clients) == 1 &&
1059 !client->internal->params->nickname_force_format) {
1060 silc_client_list_free(client, conn, clients);
1061 return client_entry;
1066 while ((entry = silc_dlist_get(clients))) {
1067 if (entry->internal.valid && entry != client_entry)
1069 if (entry->internal.valid && entry != client_entry &&
1070 silc_utf8_strcasecmp(entry->nickname, client_entry->nickname)) {
1072 unformatted = entry;
1076 if (!len || freebase) {
1077 silc_client_list_free(client, conn, clients);
1078 return client_entry;
1081 /* If priority formatting, this client always gets unformatted nickname. */
1082 if (unformatted && priority)
1083 client_entry = unformatted;
1085 memset(newnick, 0, sizeof(newnick));
1086 cp = client->internal->params->nickname_format;
1096 if (!client_entry->nickname[0])
1098 len = strlen(client_entry->nickname);
1099 memcpy(&newnick[off], client_entry->nickname, len);
1103 /* Stripped hostname */
1104 if (!client_entry->hostname[0])
1106 len = strcspn(client_entry->hostname, ".");
1107 i = strcspn(client_entry->hostname, "-");
1110 memcpy(&newnick[off], client_entry->hostname, len);
1115 if (!client_entry->hostname[0])
1117 len = strlen(client_entry->hostname);
1118 memcpy(&newnick[off], client_entry->hostname, len);
1122 /* Stripped server name */
1123 if (!client_entry->server)
1125 len = strcspn(client_entry->server, ".");
1126 memcpy(&newnick[off], client_entry->server, len);
1130 /* Full server name */
1131 if (!client_entry->server)
1133 len = strlen(client_entry->server);
1134 memcpy(&newnick[off], client_entry->server, len);
1138 /* Ascending number */
1143 if (silc_dlist_count(clients) == 1)
1146 silc_dlist_start(clients);
1147 while ((entry = silc_dlist_get(clients))) {
1148 if (!silc_utf8_strncasecmp(entry->nickname, newnick, off))
1150 if (strlen(entry->nickname) <= off)
1152 num = atoi(&entry->nickname[off]);
1157 memset(tmp, 0, sizeof(tmp));
1158 silc_snprintf(tmp, sizeof(tmp) - 1, "%d", ++max);
1160 memcpy(&newnick[off], tmp, len);
1165 /* Some other character in the string */
1166 memcpy(&newnick[off], cp, 1);
1175 memcpy(client_entry->nickname, newnick, strlen(newnick));
1176 silc_client_list_free(client, conn, clients);
1178 return client_entry;
1181 /* Parses nickname according to nickname format string */
1183 SilcBool silc_client_nickname_parse(SilcClient client,
1184 SilcClientConnection conn,
1188 char *cp, s = 0, e = 0, *nick;
1192 if (!client->internal->params->nickname_format[0])
1195 if (!nickname || !nickname[0])
1198 cp = client->internal->params->nickname_format;
1218 /* Get separator character */
1231 /* Parse the nickname */
1235 if (strchr(nickname, s))
1236 nick = strchr(nickname, s) + 1;
1238 if (strchr(nick, e))
1239 len = strchr(nick, e) - nick;
1243 *ret_nick = silc_memdup(nick, len);
1247 SILC_LOG_DEBUG(("Parsed nickname: %s", *ret_nick));
1252 /************************ Channel Searching Locally *************************/
1254 /* Finds entry for channel by the channel name. Returns the entry or NULL
1255 if the entry was not found. It is found only if the client is joined
1258 SilcChannelEntry silc_client_get_channel(SilcClient client,
1259 SilcClientConnection conn,
1262 SilcIDCacheEntry id_cache;
1263 SilcChannelEntry entry;
1265 if (!client || !conn || !channel)
1268 SILC_LOG_DEBUG(("Find channel %s", channel));
1270 /* Normalize name for search */
1271 channel = silc_channel_name_check(channel, strlen(channel), SILC_STRING_UTF8,
1276 silc_mutex_lock(conn->internal->lock);
1278 if (!silc_idcache_find_by_name_one(conn->internal->channel_cache, channel,
1280 silc_mutex_unlock(conn->internal->lock);
1285 SILC_LOG_DEBUG(("Found"));
1287 entry = id_cache->context;
1290 silc_client_ref_channel(client, conn, entry);
1291 silc_mutex_unlock(conn->internal->lock);
1298 /* Finds entry for channel by the channel ID. Returns the entry or NULL
1299 if the entry was not found. It is found only if the client is joined
1302 SilcChannelEntry silc_client_get_channel_by_id(SilcClient client,
1303 SilcClientConnection conn,
1304 SilcChannelID *channel_id)
1306 SilcIDCacheEntry id_cache;
1307 SilcChannelEntry entry;
1309 if (!client || !conn || !channel_id)
1312 SILC_LOG_DEBUG(("Find channel by id %s",
1313 silc_id_render(channel_id, SILC_ID_CHANNEL)));
1315 silc_mutex_lock(conn->internal->lock);
1317 if (!silc_idcache_find_by_id_one(conn->internal->channel_cache, channel_id,
1319 silc_mutex_unlock(conn->internal->lock);
1323 SILC_LOG_DEBUG(("Found"));
1325 entry = id_cache->context;
1328 silc_client_ref_channel(client, conn, entry);
1329 silc_mutex_unlock(conn->internal->lock);
1334 /********************** Channel Resolving from Server ***********************/
1336 /* Channel resolving context */
1339 SilcGetChannelCallback completion;
1341 } *SilcClientGetChannelInternal;
1343 /* Resolving command callback */
1345 static SilcBool silc_client_get_channel_cb(SilcClient client,
1346 SilcClientConnection conn,
1347 SilcCommand command,
1353 SilcClientGetChannelInternal i = context;
1354 SilcChannelEntry entry;
1356 if (error != SILC_STATUS_OK) {
1357 SILC_LOG_DEBUG(("Resolving failed: %s", silc_get_status_message(error)));
1359 i->completion(client, conn, error, NULL, i->context);
1363 /* Add the returned channel to list */
1364 if (i->completion) {
1365 entry = va_arg(ap, SilcChannelEntry);
1366 silc_client_ref_channel(client, conn, entry);
1367 silc_dlist_add(i->channels, entry);
1370 if (status == SILC_STATUS_OK || status == SILC_STATUS_LIST_END) {
1371 /* Deliver the channels to the caller */
1372 if (i->completion) {
1373 SILC_LOG_DEBUG(("Resolved %d channels", silc_dlist_count(i->channels)));
1374 silc_dlist_start(i->channels);
1375 i->completion(client, conn, SILC_STATUS_OK, i->channels, i->context);
1383 silc_client_list_free_channels(client, conn, i->channels);
1388 /* Resolves channel entry from the server by the channel name. */
1390 void silc_client_get_channel_resolve(SilcClient client,
1391 SilcClientConnection conn,
1393 SilcGetChannelCallback completion,
1396 SilcClientGetChannelInternal i;
1398 if (!client || !conn || !channel_name || !completion)
1401 SILC_LOG_DEBUG(("Resolve channel %s", channel_name));
1403 i = silc_calloc(1, sizeof(*i));
1406 i->completion = completion;
1407 i->context = context;
1408 i->channels = silc_dlist_init();
1414 /* Send the command */
1415 if (!silc_client_command_send(client, conn, SILC_COMMAND_IDENTIFY,
1416 silc_client_get_channel_cb, i, 1,
1417 3, channel_name, strlen(channel_name))) {
1419 completion(client, conn, SILC_STATUS_ERR_RESOURCE_LIMIT, NULL, context);
1423 /* Resolves channel information from the server by the channel ID. */
1426 silc_client_get_channel_by_id_resolve(SilcClient client,
1427 SilcClientConnection conn,
1428 SilcChannelID *channel_id,
1429 SilcGetChannelCallback completion,
1432 SilcClientGetChannelInternal i;
1434 SilcUInt16 cmd_ident;
1436 if (!client || !conn || !channel_id || !completion)
1439 SILC_LOG_DEBUG(("Resolve channel by id %s",
1440 silc_id_render(channel_id, SILC_ID_CHANNEL)));
1442 i = silc_calloc(1, sizeof(*i));
1445 i->completion = completion;
1446 i->context = context;
1447 i->channels = silc_dlist_init();
1453 /* Send the command */
1454 idp = silc_id_payload_encode(channel_id, SILC_ID_CHANNEL);
1455 cmd_ident = silc_client_command_send(client, conn, SILC_COMMAND_IDENTIFY,
1456 silc_client_get_channel_cb, i, 1,
1457 5, silc_buffer_datalen(idp));
1458 silc_buffer_free(idp);
1459 if (!cmd_ident && completion)
1460 completion(client, conn, SILC_STATUS_ERR_RESOURCE_LIMIT, NULL, context);
1465 /************************* Channel Entry Routines ***************************/
1467 /* Add new channel entry to the ID Cache */
1469 SilcChannelEntry silc_client_add_channel(SilcClient client,
1470 SilcClientConnection conn,
1471 const char *channel_name,
1473 SilcChannelID *channel_id)
1475 SilcChannelEntry channel;
1476 char *channel_namec;
1478 SILC_LOG_DEBUG(("Start"));
1480 channel = silc_calloc(1, sizeof(*channel));
1484 silc_rwlock_alloc(&channel->internal.lock);
1485 silc_atomic_init16(&channel->internal.refcnt, 0);
1486 channel->id = *channel_id;
1487 channel->mode = mode;
1489 channel->channel_name = strdup(channel_name);
1490 if (!channel->channel_name) {
1495 channel->user_list = silc_hash_table_alloc(1, silc_hash_ptr, NULL, NULL,
1496 NULL, NULL, NULL, TRUE);
1497 if (!channel->user_list) {
1498 silc_free(channel->channel_name);
1503 /* Normalize channel name */
1504 channel_namec = silc_channel_name_check(channel_name, strlen(channel_name),
1505 SILC_STRING_UTF8, 256, NULL);
1506 if (!channel_namec) {
1507 silc_free(channel->channel_name);
1508 silc_hash_table_free(channel->user_list);
1513 silc_mutex_lock(conn->internal->lock);
1515 /* Add channel to cache, the normalized channel name is saved to cache */
1516 if (!silc_idcache_add(conn->internal->channel_cache, channel_namec,
1517 &channel->id, channel)) {
1518 silc_free(channel_namec);
1519 silc_free(channel->channel_name);
1520 silc_hash_table_free(channel->user_list);
1522 silc_mutex_unlock(conn->internal->lock);
1526 silc_mutex_unlock(conn->internal->lock);
1527 silc_client_ref_channel(client, conn, channel);
1529 SILC_LOG_DEBUG(("Added %p", channel));
1534 /* Removes channel from the cache by the channel entry. */
1536 SilcBool silc_client_del_channel(SilcClient client, SilcClientConnection conn,
1537 SilcChannelEntry channel)
1539 SilcIDCacheEntry id_cache;
1547 if (silc_atomic_sub_int16(&channel->internal.refcnt, 1) > 0)
1550 SILC_LOG_DEBUG(("Deleting channel %p", channel));
1552 silc_mutex_lock(conn->internal->lock);
1553 if (silc_idcache_find_by_context(conn->internal->channel_cache, channel,
1555 silc_free(id_cache->name);
1556 ret = silc_idcache_del_by_context(conn->internal->channel_cache,
1558 silc_mutex_unlock(conn->internal->lock);
1563 silc_client_empty_channel(client, conn, channel);
1564 silc_hash_table_free(channel->user_list);
1565 silc_free(channel->channel_name);
1566 silc_free(channel->topic);
1567 if (channel->founder_key)
1568 silc_pkcs_public_key_free(channel->founder_key);
1569 if (channel->internal.send_key)
1570 silc_cipher_free(channel->internal.send_key);
1571 if (channel->internal.receive_key)
1572 silc_cipher_free(channel->internal.receive_key);
1573 if (channel->internal.hmac)
1574 silc_hmac_free(channel->internal.hmac);
1575 if (channel->internal.old_channel_keys) {
1576 silc_dlist_start(channel->internal.old_channel_keys);
1577 while ((key = silc_dlist_get(channel->internal.old_channel_keys)))
1578 silc_cipher_free(key);
1579 silc_dlist_uninit(channel->internal.old_channel_keys);
1581 if (channel->internal.old_hmacs) {
1582 silc_dlist_start(channel->internal.old_hmacs);
1583 while ((hmac = silc_dlist_get(channel->internal.old_hmacs)))
1584 silc_hmac_free(hmac);
1585 silc_dlist_uninit(channel->internal.old_hmacs);
1587 if (channel->channel_pubkeys)
1588 silc_argument_list_free(channel->channel_pubkeys,
1589 SILC_ARGUMENT_PUBLIC_KEY);
1590 silc_client_del_channel_private_keys(client, conn, channel);
1591 silc_atomic_uninit16(&channel->internal.refcnt);
1592 silc_rwlock_free(channel->internal.lock);
1593 silc_schedule_task_del_by_context(conn->client->schedule, channel);
1599 /* Replaces the channel ID of the `channel' to `new_id'. Returns FALSE
1600 if the ID could not be changed. This handles entry locking internally. */
1602 SilcBool silc_client_replace_channel_id(SilcClient client,
1603 SilcClientConnection conn,
1604 SilcChannelEntry channel,
1605 SilcChannelID *new_id)
1607 SilcBool ret = FALSE;
1612 SILC_LOG_DEBUG(("Old Channel ID id(%s)",
1613 silc_id_render(&channel->id, SILC_ID_CHANNEL)));
1614 SILC_LOG_DEBUG(("New Channel ID id(%s)",
1615 silc_id_render(new_id, SILC_ID_CHANNEL)));
1618 silc_rwlock_wrlock(channel->internal.lock);
1619 silc_mutex_lock(conn->internal->lock);
1620 silc_idcache_update_by_context(conn->internal->channel_cache, channel,
1621 new_id, NULL, FALSE);
1622 silc_mutex_unlock(conn->internal->lock);
1623 silc_rwlock_unlock(channel->internal.lock);
1630 void silc_client_lock_channel(SilcChannelEntry channel_entry)
1632 silc_rwlock_rdlock(channel_entry->internal.lock);
1635 /* Unlock channel */
1637 void silc_client_unlock_channel(SilcChannelEntry channel_entry)
1639 silc_rwlock_unlock(channel_entry->internal.lock);
1642 /* Take reference of channel entry */
1644 SilcChannelEntry silc_client_ref_channel(SilcClient client,
1645 SilcClientConnection conn,
1646 SilcChannelEntry channel_entry)
1648 silc_atomic_add_int16(&channel_entry->internal.refcnt, 1);
1649 SILC_LOG_DEBUG(("Channel %p refcnt %d->%d", channel_entry,
1650 silc_atomic_get_int16(&channel_entry->internal.refcnt) - 1,
1651 silc_atomic_get_int16(&channel_entry->internal.refcnt)));
1652 return channel_entry;
1655 /* Release reference of channel entry */
1657 void silc_client_unref_channel(SilcClient client, SilcClientConnection conn,
1658 SilcChannelEntry channel_entry)
1660 if (channel_entry) {
1661 SILC_LOG_DEBUG(("Channel %p refcnt %d->%d", channel_entry,
1662 silc_atomic_get_int16(&channel_entry->internal.refcnt),
1663 silc_atomic_get_int16(&channel_entry->internal.refcnt)
1665 silc_client_del_channel(client, conn, channel_entry);
1669 /* Free channel entry list */
1671 void silc_client_list_free_channels(SilcClient client,
1672 SilcClientConnection conn,
1673 SilcDList channel_list)
1675 SilcChannelEntry channel_entry;
1678 silc_dlist_start(channel_list);
1679 while ((channel_entry = silc_dlist_get(channel_list)))
1680 silc_client_unref_channel(client, conn, channel_entry);
1682 silc_dlist_uninit(channel_list);
1686 /************************* Server Searching Locally *************************/
1688 /* Finds entry for server by the server name. */
1690 SilcServerEntry silc_client_get_server(SilcClient client,
1691 SilcClientConnection conn,
1694 SilcIDCacheEntry id_cache;
1695 SilcServerEntry entry;
1697 if (!client || !conn || !server_name)
1700 SILC_LOG_DEBUG(("Find server by name %s", server_name));
1702 /* Normalize server name for search */
1703 server_name = silc_identifier_check(server_name, strlen(server_name),
1704 SILC_STRING_UTF8, 256, NULL);
1708 silc_mutex_lock(conn->internal->lock);
1710 if (!silc_idcache_find_by_name_one(conn->internal->server_cache,
1711 server_name, &id_cache)) {
1712 silc_free(server_name);
1713 silc_mutex_unlock(conn->internal->lock);
1717 SILC_LOG_DEBUG(("Found"));
1720 entry = id_cache->context;
1721 silc_client_ref_server(client, conn, entry);
1723 silc_mutex_unlock(conn->internal->lock);
1725 silc_free(server_name);
1730 /* Finds entry for server by the server ID. */
1732 SilcServerEntry silc_client_get_server_by_id(SilcClient client,
1733 SilcClientConnection conn,
1734 SilcServerID *server_id)
1736 SilcIDCacheEntry id_cache;
1737 SilcServerEntry entry;
1739 if (!client || !conn || !server_id)
1742 SILC_LOG_DEBUG(("Find server by id %s",
1743 silc_id_render(server_id, SILC_ID_SERVER)));
1745 silc_mutex_lock(conn->internal->lock);
1747 if (!silc_idcache_find_by_id_one(conn->internal->server_cache,
1748 server_id, &id_cache)) {
1749 silc_mutex_unlock(conn->internal->lock);
1753 SILC_LOG_DEBUG(("Found"));
1756 entry = id_cache->context;
1757 silc_client_ref_server(client, conn, entry);
1759 silc_mutex_unlock(conn->internal->lock);
1764 /*********************** Server Resolving from Server ***********************/
1766 /* Resolving context */
1769 SilcGetServerCallback completion;
1771 } *SilcClientGetServerInternal;
1773 /* Resolving command callback */
1775 static SilcBool silc_client_get_server_cb(SilcClient client,
1776 SilcClientConnection conn,
1777 SilcCommand command,
1783 SilcClientGetServerInternal i = context;
1784 SilcServerEntry server;
1786 if (error != SILC_STATUS_OK) {
1787 SILC_LOG_DEBUG(("Resolving failed: %s", silc_get_status_message(error)));
1789 i->completion(client, conn, error, NULL, i->context);
1793 /* Add the returned servers to list */
1794 if (i->completion) {
1795 server = va_arg(ap, SilcServerEntry);
1796 silc_client_ref_server(client, conn, server);
1797 silc_dlist_add(i->servers, server);
1798 server->internal.resolve_cmd_ident = 0;
1801 if (status == SILC_STATUS_OK || status == SILC_STATUS_LIST_END) {
1802 /* Deliver the servers to the caller */
1803 if (i->completion) {
1804 SILC_LOG_DEBUG(("Resolved %d servers", silc_dlist_count(i->servers)));
1805 silc_dlist_start(i->servers);
1806 i->completion(client, conn, SILC_STATUS_OK, i->servers, i->context);
1814 silc_client_list_free_servers(client, conn, i->servers);
1819 /* Resolve server by server ID */
1822 silc_client_get_server_by_id_resolve(SilcClient client,
1823 SilcClientConnection conn,
1824 SilcServerID *server_id,
1825 SilcGetServerCallback completion,
1828 SilcClientGetServerInternal i;
1829 SilcServerEntry server;
1831 SilcUInt16 cmd_ident;
1833 if (!client || !conn || !server_id || !completion)
1836 SILC_LOG_DEBUG(("Resolve server by id %s",
1837 silc_id_render(server_id, SILC_ID_SERVER)));
1839 i = silc_calloc(1, sizeof(*i));
1842 i->completion = completion;
1843 i->context = context;
1844 i->servers = silc_dlist_init();
1850 /* Attach to resolving, if on going */
1851 server = silc_client_get_server_by_id(client, conn, server_id);
1852 if (server && server->internal.resolve_cmd_ident) {
1853 SILC_LOG_DEBUG(("Attach to existing resolving"));
1854 silc_client_unref_server(client, conn, server);
1855 silc_client_command_pending(conn, SILC_COMMAND_NONE,
1856 server->internal.resolve_cmd_ident,
1857 silc_client_get_server_cb, i);
1858 return server->internal.resolve_cmd_ident;
1861 /* Send the command */
1862 idp = silc_id_payload_encode(server_id, SILC_ID_SERVER);
1863 cmd_ident = silc_client_command_send(client, conn, SILC_COMMAND_IDENTIFY,
1864 silc_client_get_server_cb, i, 1,
1865 5, silc_buffer_datalen(idp));
1866 silc_buffer_free(idp);
1867 if (!cmd_ident && completion)
1868 completion(client, conn, SILC_STATUS_ERR_RESOURCE_LIMIT, NULL, context);
1870 if (server && cmd_ident)
1871 server->internal.resolve_cmd_ident = cmd_ident;
1873 silc_client_unref_server(client, conn, server);
1878 /************************** Server Entry Routines ***************************/
1880 /* Add new server entry */
1882 SilcServerEntry silc_client_add_server(SilcClient client,
1883 SilcClientConnection conn,
1884 const char *server_name,
1885 const char *server_info,
1886 SilcServerID *server_id)
1888 SilcServerEntry server_entry;
1889 char *server_namec = NULL;
1894 SILC_LOG_DEBUG(("Adding new server %s", server_name));
1896 server_entry = silc_calloc(1, sizeof(*server_entry));
1900 silc_rwlock_alloc(&server_entry->internal.lock);
1901 silc_atomic_init8(&server_entry->internal.refcnt, 0);
1902 server_entry->id = *server_id;
1904 server_entry->server_name = strdup(server_name);
1906 server_entry->server_info = strdup(server_info);
1908 /* Normalize server name */
1910 server_namec = silc_identifier_check(server_name, strlen(server_name),
1911 SILC_STRING_UTF8, 256, NULL);
1912 if (!server_namec) {
1913 silc_free(server_entry->server_name);
1914 silc_free(server_entry->server_info);
1915 silc_free(server_entry);
1920 silc_mutex_lock(conn->internal->lock);
1922 /* Add server to cache */
1923 if (!silc_idcache_add(conn->internal->server_cache, server_namec,
1924 &server_entry->id, server_entry)) {
1925 silc_free(server_namec);
1926 silc_free(server_entry->server_name);
1927 silc_free(server_entry->server_info);
1928 silc_free(server_entry);
1929 silc_mutex_unlock(conn->internal->lock);
1933 silc_mutex_unlock(conn->internal->lock);
1934 silc_client_ref_server(client, conn, server_entry);
1936 SILC_LOG_DEBUG(("Added %p", server_entry));
1938 return server_entry;
1941 /* Removes server from the cache by the server entry. */
1943 SilcBool silc_client_del_server(SilcClient client, SilcClientConnection conn,
1944 SilcServerEntry server)
1946 SilcIDCacheEntry id_cache;
1952 if (silc_atomic_sub_int8(&server->internal.refcnt, 1) > 0)
1955 SILC_LOG_DEBUG(("Deleting server %p", server));
1957 silc_mutex_lock(conn->internal->lock);
1958 if (silc_idcache_find_by_context(conn->internal->server_cache, server,
1960 silc_free(id_cache->name);
1961 ret = silc_idcache_del_by_context(conn->internal->server_cache,
1963 silc_mutex_unlock(conn->internal->lock);
1965 silc_free(server->server_name);
1966 silc_free(server->server_info);
1967 if (server->public_key)
1968 silc_pkcs_public_key_free(server->public_key);
1969 silc_atomic_uninit8(&server->internal.refcnt);
1970 silc_rwlock_free(server->internal.lock);
1976 /* Updates the `server_entry' with the new information sent as argument. */
1978 void silc_client_update_server(SilcClient client,
1979 SilcClientConnection conn,
1980 SilcServerEntry server_entry,
1981 const char *server_name,
1982 const char *server_info)
1984 char *server_namec = NULL;
1986 SILC_LOG_DEBUG(("Updating server %p", server_entry));
1989 (!server_entry->server_name ||
1990 !silc_utf8_strcasecmp(server_entry->server_name, server_name))) {
1992 server_namec = silc_identifier_check(server_name, strlen(server_name),
1993 SILC_STRING_UTF8, 256, NULL);
1997 silc_free(server_entry->server_name);
1998 server_entry->server_name = strdup(server_name);
1999 if (!server_entry->server_name)
2002 /* Update cache entry */
2003 silc_mutex_lock(conn->internal->lock);
2004 silc_idcache_update_by_context(conn->internal->server_cache, server_entry,
2005 NULL, server_namec, TRUE);
2006 silc_mutex_unlock(conn->internal->lock);
2010 (!server_entry->server_info ||
2011 !silc_utf8_strcasecmp(server_entry->server_info, server_info))) {
2012 silc_free(server_entry->server_info);
2013 server_entry->server_info = strdup(server_info);
2019 void silc_client_lock_server(SilcServerEntry server_entry)
2021 silc_rwlock_rdlock(server_entry->internal.lock);
2026 void silc_client_unlock_server(SilcServerEntry server_entry)
2028 silc_rwlock_unlock(server_entry->internal.lock);
2031 /* Take reference of server entry */
2033 SilcServerEntry silc_client_ref_server(SilcClient client,
2034 SilcClientConnection conn,
2035 SilcServerEntry server_entry)
2037 silc_atomic_add_int8(&server_entry->internal.refcnt, 1);
2038 SILC_LOG_DEBUG(("Server %p refcnt %d->%d", server_entry,
2039 silc_atomic_get_int8(&server_entry->internal.refcnt) - 1,
2040 silc_atomic_get_int8(&server_entry->internal.refcnt)));
2041 return server_entry;
2044 /* Release reference of server entry */
2046 void silc_client_unref_server(SilcClient client, SilcClientConnection conn,
2047 SilcServerEntry server_entry)
2050 SILC_LOG_DEBUG(("Server %p refcnt %d->%d", server_entry,
2051 silc_atomic_get_int8(&server_entry->internal.refcnt),
2052 silc_atomic_get_int8(&server_entry->internal.refcnt)
2054 silc_client_del_server(client, conn, server_entry);
2058 /* Free server entry list */
2060 void silc_client_list_free_servers(SilcClient client,
2061 SilcClientConnection conn,
2062 SilcDList server_list)
2064 SilcServerEntry server_entry;
2067 silc_dlist_start(server_list);
2068 while ((server_entry = silc_dlist_get(server_list)))
2069 silc_client_unref_server(client, conn, server_entry);
2071 silc_dlist_uninit(server_list);