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_LOG_DEBUG(("Found %d clients", silc_dlist_count(clients)));
162 silc_dlist_start(clients);
166 /* Finds clients by nickname from local cache. */
168 SilcDList silc_client_get_clients_local(SilcClient client,
169 SilcClientConnection conn,
170 const char *nickname,
173 return silc_client_get_clients_local_ext(client, conn, nickname, return_all,
177 /********************** Client Resolving from Server ************************/
179 /* Resolving context */
182 SilcGetClientCallback completion;
184 SilcClientEntry client_entry;
185 } *SilcClientGetClientInternal;
187 /* Resolving command callback */
189 static SilcBool silc_client_get_clients_cb(SilcClient client,
190 SilcClientConnection conn,
197 SilcClientGetClientInternal i = context;
198 SilcClientEntry client_entry;
200 if (error != SILC_STATUS_OK) {
201 SILC_LOG_DEBUG(("Resolving failed: %s", silc_get_status_message(error)));
203 if (i->client_entry) {
204 i->client_entry->internal.resolve_cmd_ident = 0;
205 silc_client_unref_client(client, conn, i->client_entry);
209 i->completion(client, conn, error, NULL, i->context);
213 /* Add the returned client to list */
215 client_entry = va_arg(ap, SilcClientEntry);
216 silc_client_ref_client(client, conn, client_entry);
217 silc_dlist_add(i->clients, client_entry);
218 client_entry->internal.resolve_cmd_ident = 0;
221 if (status == SILC_STATUS_OK || status == SILC_STATUS_LIST_END) {
222 /* Deliver the clients to the caller */
224 SILC_LOG_DEBUG(("Resolved %d clients", silc_dlist_count(i->clients)));
226 if (i->client_entry) {
227 i->client_entry->internal.resolve_cmd_ident = 0;
228 silc_client_unref_client(client, conn, i->client_entry);
231 silc_dlist_start(i->clients);
232 i->completion(client, conn, SILC_STATUS_OK, i->clients, i->context);
240 silc_client_list_free(client, conn, i->clients);
245 /* Resolves client information from server by the client ID. */
248 silc_client_get_client_by_id_resolve(SilcClient client,
249 SilcClientConnection conn,
250 SilcClientID *client_id,
251 SilcBuffer attributes,
252 SilcGetClientCallback completion,
255 SilcClientGetClientInternal i;
256 SilcClientEntry client_entry;
258 SilcUInt16 cmd_ident;
260 if (!client || !conn | !client_id)
263 SILC_LOG_DEBUG(("Resolve client by ID (%s)",
264 silc_id_render(client_id, SILC_ID_CLIENT)));
266 i = silc_calloc(1, sizeof(*i));
269 i->completion = completion;
270 i->context = context;
271 i->clients = silc_dlist_init();
277 /* Attach to resolving, if on going */
278 client_entry = silc_client_get_client_by_id(client, conn, client_id);
279 if (client_entry && client_entry->internal.resolve_cmd_ident) {
280 SILC_LOG_DEBUG(("Attach to existing resolving"));
281 silc_client_unref_client(client, conn, client_entry);
282 silc_client_command_pending(conn, SILC_COMMAND_NONE,
283 client_entry->internal.resolve_cmd_ident,
284 silc_client_get_clients_cb, i);
285 return client_entry->internal.resolve_cmd_ident;
288 /* Send the command */
289 idp = silc_id_payload_encode(client_id, SILC_ID_CLIENT);
290 cmd_ident = silc_client_command_send(client, conn, SILC_COMMAND_WHOIS,
291 silc_client_get_clients_cb, i,
292 2, 3, silc_buffer_datalen(attributes),
293 4, silc_buffer_datalen(idp));
294 if (!cmd_ident && completion)
295 completion(client, conn, SILC_STATUS_ERR_RESOURCE_LIMIT, NULL, context);
297 if (client_entry && cmd_ident) {
298 client_entry->internal.resolve_cmd_ident = cmd_ident;
299 i->client_entry = client_entry;
301 silc_client_unref_client(client, conn, client_entry);
304 silc_buffer_free(idp);
309 /* Finds client entry or entries by the `nickname' and `server'. The
310 completion callback will be called when the client entries has been
311 found. Used internally by the library. */
313 static SilcUInt16 silc_client_get_clients_i(SilcClient client,
314 SilcClientConnection conn,
316 const char *nickname,
318 SilcBuffer attributes,
319 SilcGetClientCallback completion,
322 SilcClientGetClientInternal i;
323 char userhost[768 + 1];
326 SILC_LOG_DEBUG(("Resolve client by %s command",
327 silc_get_command_name(command)));
329 if (!client || !conn)
331 if (!nickname && !attributes)
334 i = silc_calloc(1, sizeof(*i));
337 i->clients = silc_dlist_init();
342 i->completion = completion;
343 i->context = context;
345 memset(userhost, 0, sizeof(userhost));
346 if (nickname && server) {
347 len = strlen(nickname) + strlen(server) + 3;
348 silc_strncat(userhost, len, nickname, strlen(nickname));
349 silc_strncat(userhost, len, "@", 1);
350 silc_strncat(userhost, len, server, strlen(server));
351 } else if (nickname) {
352 silc_strncat(userhost, sizeof(userhost) - 1, nickname, strlen(nickname));
355 /* Send the command */
356 if (command == SILC_COMMAND_IDENTIFY)
357 return silc_client_command_send(client, conn, SILC_COMMAND_IDENTIFY,
358 silc_client_get_clients_cb, i,
359 1, 1, userhost, strlen(userhost));
360 return silc_client_command_send(client, conn, SILC_COMMAND_WHOIS,
361 silc_client_get_clients_cb, i,
362 2, 1, userhost, strlen(userhost),
363 3, silc_buffer_datalen(attributes));
366 /* Get clients from server with IDENTIFY command */
368 SilcUInt16 silc_client_get_clients(SilcClient client,
369 SilcClientConnection conn,
370 const char *nickname,
372 SilcGetClientCallback completion,
375 return silc_client_get_clients_i(client, conn, SILC_COMMAND_IDENTIFY,
376 nickname, server, NULL,
377 completion, context);
380 /* Get clients from server with WHOIS command */
382 SilcUInt16 silc_client_get_clients_whois(SilcClient client,
383 SilcClientConnection conn,
384 const char *nickname,
386 SilcBuffer attributes,
387 SilcGetClientCallback completion,
390 return silc_client_get_clients_i(client, conn, SILC_COMMAND_WHOIS,
391 nickname, server, attributes,
392 completion, context);
395 /* ID list resolving context */
397 SilcGetClientCallback completion;
399 SilcBuffer client_id_list;
400 SilcUInt32 list_count;
401 } *GetClientsByListInternal;
403 static SilcBool silc_client_get_clients_list_cb(SilcClient client,
404 SilcClientConnection conn,
411 GetClientsByListInternal i = context;
412 SilcClientEntry client_entry;
418 /* Process the list after all replies have been received */
419 if (status != SILC_STATUS_OK && !SILC_STATUS_IS_ERROR(status) &&
420 status != SILC_STATUS_LIST_END)
423 SILC_LOG_DEBUG(("Resolved all clients"));
425 clients = silc_dlist_init();
427 status = SILC_STATUS_ERR_RESOURCE_LIMIT;
431 for (c = 0; c < i->list_count; c++) {
433 SILC_GET16_MSB(idp_len, i->client_id_list->data + 2);
435 if (!silc_id_payload_parse_id(i->client_id_list->data, idp_len, &id)) {
436 status = SILC_STATUS_ERR_BAD_CLIENT_ID;
440 /* Get client entry */
441 client_entry = silc_client_get_client_by_id(client, conn, &id.u.client_id);
443 silc_dlist_add(clients, client_entry);
445 if (!silc_buffer_pull(i->client_id_list, idp_len)) {
446 status = SILC_STATUS_ERR_BAD_CLIENT_ID;
451 silc_dlist_start(clients);
452 status = SILC_STATUS_OK;
454 i->completion(client, conn, status, clients, i->context);
457 if (status != SILC_STATUS_OK && i->completion)
458 i->completion(client, conn, status, NULL, i->context);
460 silc_client_list_free(client, conn, clients);
461 silc_buffer_free(i->client_id_list);
467 /* Gets client entries by the list of client ID's `client_id_list'. This
468 always resolves those client ID's it does not know yet from the server
469 so this function might take a while. The `client_id_list' is a list
470 of ID Payloads added one after other. JOIN command reply and USERS
471 command reply for example returns this sort of list. The `completion'
472 will be called after the entries are available. */
474 SilcUInt16 silc_client_get_clients_by_list(SilcClient client,
475 SilcClientConnection conn,
476 SilcUInt32 list_count,
477 SilcBuffer client_id_list,
478 SilcGetClientCallback completion,
481 GetClientsByListInternal in;
482 SilcClientEntry entry;
483 unsigned char **res_argv = NULL;
484 SilcUInt32 *res_argv_lens = NULL, *res_argv_types = NULL, res_argc = 0;
485 SilcUInt16 idp_len, cmd_ident;
490 SILC_LOG_DEBUG(("Resolve clients from Client ID list"));
492 if (!client || !conn || !client_id_list)
495 in = silc_calloc(1, sizeof(*in));
498 in->completion = completion;
499 in->context = context;
500 in->list_count = list_count;
501 in->client_id_list = silc_buffer_copy(client_id_list);
502 if (!in->client_id_list)
505 for (i = 0; i < list_count; i++) {
507 SILC_GET16_MSB(idp_len, client_id_list->data + 2);
509 if (!silc_id_payload_parse_id(client_id_list->data, idp_len, &id))
512 /* Check if we have this client cached already. If we don't have the
513 entry or it has incomplete info, then resolve it from the server. */
514 entry = silc_client_get_client_by_id(client, conn, &id.u.client_id);
515 if (!entry || !entry->nickname[0] || !entry->username[0] ||
518 res_argv = silc_calloc(list_count, sizeof(*res_argv));
519 res_argv_lens = silc_calloc(list_count, sizeof(*res_argv_lens));
520 res_argv_types = silc_calloc(list_count, sizeof(*res_argv_types));
521 if (!res_argv || !res_argv_lens || !res_argv_types) {
522 silc_client_unref_client(client, conn, entry);
527 res_argv[res_argc] = client_id_list->data;
528 res_argv_lens[res_argc] = idp_len;
529 res_argv_types[res_argc] = res_argc + 4;
532 silc_client_unref_client(client, conn, entry);
534 if (!silc_buffer_pull(client_id_list, idp_len))
537 silc_buffer_start(client_id_list);
539 /* Query the unknown client information from server */
541 cmd_ident = silc_client_command_send_argv(client,
542 conn, SILC_COMMAND_WHOIS,
543 silc_client_get_clients_list_cb,
544 in, res_argc, res_argv,
548 silc_free(res_argv_lens);
549 silc_free(res_argv_types);
553 /* We have the clients in cache, get them and call the completion */
554 silc_client_get_clients_list_cb(client, conn, SILC_COMMAND_WHOIS,
555 SILC_STATUS_OK, SILC_STATUS_OK, in, tmp);
559 silc_buffer_free(in->client_id_list);
562 silc_free(res_argv_lens);
563 silc_free(res_argv_types);
570 SilcClientConnection conn;
571 SilcChannelID channel_id;
572 SilcGetClientCallback completion;
575 } *GetClientsByChannelInternal;
577 SILC_CLIENT_CMD_FUNC(get_clients_by_channel_cb)
579 GetClientsByChannelInternal i = context;
580 SilcClientEntry *clients = NULL;
581 SilcUInt32 clients_count = 0;
582 SilcBool found = FALSE;
583 SilcChannelEntry channel;
584 SilcHashTableList htl;
593 channel = silc_client_get_channel_by_id(i->client, i->conn, &i->channel_id);
594 if (channel && !silc_hash_table_count(channel->user_list)) {
595 clients = silc_calloc(silc_hash_table_count(channel->user_list),
597 silc_hash_table_list(channel->user_list, &htl);
598 while (silc_hash_table_get(&htl, NULL, (void *)&chu))
599 clients[clients_count++] = chu->client;
600 silc_hash_table_list_reset(&htl);
605 i->completion(i->client, i->conn, clients, clients_count, i->context);
608 i->completion(i->client, i->conn, NULL, 0, i->context);
614 /* Gets client entries by the channel entry indicated by `channel'. Thus,
615 it resolves the clients currently on that channel. */
617 void silc_client_get_clients_by_channel(SilcClient client,
618 SilcClientConnection conn,
619 SilcChannelEntry channel,
620 SilcGetClientCallback completion,
623 GetClientsByChannelInternal in;
624 SilcHashTableList htl;
626 SilcClientEntry entry;
627 unsigned char **res_argv = NULL;
628 SilcUInt32 *res_argv_lens = NULL, *res_argv_types = NULL, res_argc = 0;
630 SilcBool wait_res = FALSE;
632 assert(client && conn && channel);
634 SILC_LOG_DEBUG(("Start"));
636 in = silc_calloc(1, sizeof(*in));
639 in->channel_id = *channel->id;
640 in->completion = completion;
641 in->context = context;
643 /* If user list does not exist, send USERS command. */
644 if (!channel->user_list || !silc_hash_table_count(channel->user_list)) {
645 SILC_LOG_DEBUG(("Sending USERS"));
646 silc_client_command_register(client, SILC_COMMAND_USERS, NULL, NULL,
647 silc_client_command_reply_users_i, 0,
649 silc_client_command_send(client, conn, SILC_COMMAND_USERS,
650 conn->cmd_ident, 1, 2, channel->channel_name,
651 strlen(channel->channel_name));
652 silc_client_command_pending(conn, SILC_COMMAND_USERS, conn->cmd_ident,
653 silc_client_command_get_clients_by_channel_cb,
658 silc_hash_table_list(channel->user_list, &htl);
659 while (silc_hash_table_get(&htl, NULL, (void *)&chu)) {
662 /* If the entry has incomplete info, then resolve it from the server. */
663 if (!entry->nickname[0] || !entry->realname) {
664 if (entry->status & SILC_CLIENT_STATUS_RESOLVING) {
665 /* Attach to this resolving and wait until it finishes */
666 silc_client_command_pending(
667 conn, SILC_COMMAND_NONE,
668 entry->resolve_cmd_ident,
669 silc_client_command_get_clients_by_channel_cb,
675 entry->status |= SILC_CLIENT_STATUS_RESOLVING;
676 entry->resolve_cmd_ident = conn->cmd_ident + 1;
678 idp = silc_id_payload_encode(entry->id, SILC_ID_CLIENT);
680 /* No we don't have it, query it from the server. Assemble argument
681 table that will be sent for the WHOIS command later. */
682 res_argv = silc_realloc(res_argv, sizeof(*res_argv) *
684 res_argv_lens = silc_realloc(res_argv_lens, sizeof(*res_argv_lens) *
686 res_argv_types = silc_realloc(res_argv_types, sizeof(*res_argv_types) *
688 res_argv[res_argc] = silc_memdup(idp->data, idp->len);
689 res_argv_lens[res_argc] = idp->len;
690 res_argv_types[res_argc] = res_argc + 4;
693 silc_buffer_free(idp);
696 silc_hash_table_list_reset(&htl);
698 /* Query the client information from server if the list included clients
699 that we don't know about. */
703 /* Send the WHOIS command to server */
704 res_cmd = silc_command_payload_encode(SILC_COMMAND_WHOIS,
705 res_argc, res_argv, res_argv_lens,
706 res_argv_types, ++conn->cmd_ident);
707 silc_client_packet_send(client, conn->sock, SILC_PACKET_COMMAND,
708 NULL, 0, NULL, NULL, res_cmd->data, res_cmd->len,
711 /* Register our own command reply for this command */
712 silc_client_command_register(client, SILC_COMMAND_WHOIS, NULL, NULL,
713 silc_client_command_reply_whois_i, 0,
716 /* Process the applications request after reply has been received */
717 silc_client_command_pending(
718 conn, SILC_COMMAND_WHOIS, conn->cmd_ident,
719 silc_client_command_get_clients_by_channel_cb,
723 silc_buffer_free(res_cmd);
725 silc_free(res_argv_lens);
726 silc_free(res_argv_types);
733 /* We have the clients in cache, get them and call the completion */
734 silc_client_command_get_clients_by_channel_cb((void *)in, NULL);
739 /************************** Client Entry Routines ***************************/
741 /* Creates new client entry and adds it to the ID cache. Returns pointer
744 SilcClientEntry silc_client_add_client(SilcClient client,
745 SilcClientConnection conn,
746 char *nickname, char *username,
747 char *userinfo, SilcClientID *id,
750 SilcClientEntry client_entry;
753 SILC_LOG_DEBUG(("Adding new client entry"));
755 /* Save the client infos */
756 client_entry = silc_calloc(1, sizeof(*client_entry));
760 silc_rwlock_alloc(&client_entry->internal.lock);
761 silc_atomic_init8(&client_entry->internal.refcnt, 0);
762 client_entry->id = *id;
763 client_entry->mode = mode;
764 client_entry->realname = userinfo ? strdup(userinfo) : NULL;
765 silc_parse_userfqdn(nickname, client_entry->nickname,
766 sizeof(client_entry->nickname),
767 client_entry->server,
768 sizeof(client_entry->server));
769 silc_parse_userfqdn(username, client_entry->username,
770 sizeof(client_entry->username),
771 client_entry->hostname,
772 sizeof(client_entry->hostname));
773 client_entry->channels = silc_hash_table_alloc(1, silc_hash_ptr, NULL, NULL,
774 NULL, NULL, NULL, TRUE);
775 if (!client_entry->channels) {
776 silc_free(client_entry->realname);
777 silc_free(client_entry);
781 /* Normalize nickname */
782 if (client_entry->nickname[0]) {
783 nick = silc_identifier_check(client_entry->nickname,
784 strlen(client_entry->nickname),
785 SILC_STRING_UTF8, 128, NULL);
787 silc_free(client_entry->realname);
788 silc_hash_table_free(client_entry->channels);
789 silc_free(client_entry);
794 silc_mutex_lock(conn->internal->lock);
796 /* Add client to cache, the normalized nickname is saved to cache */
797 if (!silc_idcache_add(conn->internal->client_cache, nick,
798 &client_entry->id, client_entry)) {
800 silc_free(client_entry->realname);
801 silc_hash_table_free(client_entry->channels);
802 silc_free(client_entry);
803 silc_mutex_unlock(conn->internal->lock);
807 client_entry->nickname_normalized = nick;
809 silc_mutex_unlock(conn->internal->lock);
810 silc_client_ref_client(client, conn, client_entry);
812 /* Format the nickname */
813 silc_client_nickname_format(client, conn, client_entry, FALSE);
815 if (client_entry->nickname[0])
816 client_entry->internal.valid = TRUE;
818 SILC_LOG_DEBUG(("Added %p", client_entry));
823 /* Updates the `client_entry' with the new information sent as argument.
824 This handles entry locking internally. */
826 void silc_client_update_client(SilcClient client,
827 SilcClientConnection conn,
828 SilcClientEntry client_entry,
829 const char *nickname,
830 const char *username,
831 const char *userinfo,
836 SILC_LOG_DEBUG(("Update client entry"));
838 silc_rwlock_wrlock(client_entry->internal.lock);
840 if (!client_entry->realname && userinfo)
841 client_entry->realname = strdup(userinfo);
842 if ((!client_entry->username[0] || !client_entry->hostname[0]) && username)
843 silc_parse_userfqdn(username, client_entry->username,
844 sizeof(client_entry->username),
845 client_entry->hostname,
846 sizeof(client_entry->username));
847 if (!client_entry->nickname[0] && nickname) {
848 silc_parse_userfqdn(nickname, client_entry->nickname,
849 sizeof(client_entry->nickname),
850 client_entry->server,
851 sizeof(client_entry->server));
853 /* Normalize nickname */
854 nick = silc_identifier_check(client_entry->nickname,
855 strlen(client_entry->nickname),
856 SILC_STRING_UTF8, 128, NULL);
858 silc_rwlock_unlock(client_entry->internal.lock);
862 /* Format nickname */
863 silc_client_nickname_format(client, conn, client_entry,
864 client_entry == conn->local_entry);
866 /* Update cache entry */
867 silc_mutex_lock(conn->internal->lock);
868 silc_idcache_update_by_context(conn->internal->client_cache,
869 client_entry, NULL, nick, TRUE);
870 silc_mutex_unlock(conn->internal->lock);
871 client_entry->nickname_normalized = nick;
872 client_entry->internal.valid = TRUE;
874 client_entry->mode = mode;
876 silc_rwlock_unlock(client_entry->internal.lock);
879 /* Change a client's nickname. Must be called with `client_entry' locked. */
881 SilcBool silc_client_change_nickname(SilcClient client,
882 SilcClientConnection conn,
883 SilcClientEntry client_entry,
884 const char *new_nick,
885 SilcClientID *new_id,
886 const unsigned char *idp,
891 SILC_LOG_DEBUG(("Change nickname %s to %s", client_entry->nickname,
894 /* Normalize nickname */
895 tmp = silc_identifier_check(new_nick, strlen(new_nick),
896 SILC_STRING_UTF8, 128, NULL);
900 /* Update the client entry */
901 silc_mutex_lock(conn->internal->lock);
902 if (!silc_idcache_update_by_context(conn->internal->client_cache,
903 client_entry, new_id, tmp, TRUE)) {
905 silc_mutex_unlock(conn->internal->lock);
908 silc_mutex_unlock(conn->internal->lock);
910 memset(client_entry->nickname, 0, sizeof(client_entry->nickname));
911 memcpy(client_entry->nickname, new_nick, strlen(new_nick));
912 client_entry->nickname_normalized = tmp;
913 silc_client_nickname_format(client, conn, client_entry,
914 client_entry == conn->local_entry);
916 /* For my client entry, update ID and set new ID to packet stream */
917 if (client_entry == conn->local_entry) {
918 if (idp && idp_len) {
919 silc_buffer_enlarge(conn->internal->local_idp, idp_len);
920 silc_buffer_put(conn->internal->local_idp, idp, idp_len);
923 silc_packet_set_ids(conn->stream, SILC_ID_CLIENT, conn->local_id,
927 client_entry->internal.valid = TRUE;
931 /* Deletes the client entry and frees all memory. */
933 void silc_client_del_client_entry(SilcClient client,
934 SilcClientConnection conn,
935 SilcClientEntry client_entry)
937 silc_free(client_entry->realname);
938 silc_free(client_entry->nickname_normalized);
939 silc_free(client_entry->internal.key);
940 if (client_entry->public_key)
941 silc_pkcs_public_key_free(client_entry->public_key);
942 silc_hash_table_free(client_entry->channels);
943 if (client_entry->internal.send_key)
944 silc_cipher_free(client_entry->internal.send_key);
945 if (client_entry->internal.receive_key)
946 silc_cipher_free(client_entry->internal.receive_key);
947 if (client_entry->internal.hmac_send)
948 silc_hmac_free(client_entry->internal.hmac_send);
949 if (client_entry->internal.hmac_receive)
950 silc_hmac_free(client_entry->internal.hmac_receive);
951 silc_client_ftp_session_free_client(client, client_entry);
952 if (client_entry->internal.ke)
953 silc_client_abort_key_agreement(client, conn, client_entry);
954 silc_atomic_uninit8(&client_entry->internal.refcnt);
955 silc_rwlock_free(client_entry->internal.lock);
956 silc_free(client_entry);
959 /* Removes client from the cache by the client entry. */
961 SilcBool silc_client_del_client(SilcClient client, SilcClientConnection conn,
962 SilcClientEntry client_entry)
969 if (silc_atomic_sub_int8(&client_entry->internal.refcnt, 1) > 0)
972 SILC_LOG_DEBUG(("Deleting client %p", client_entry));
974 silc_mutex_lock(conn->internal->lock);
975 ret = silc_idcache_del_by_context(conn->internal->client_cache,
977 silc_mutex_unlock(conn->internal->lock);
980 /* Remove from channels */
981 silc_client_remove_from_channels(client, conn, client_entry);
983 /* Free the client entry data */
984 silc_client_del_client_entry(client, conn, client_entry);
990 /* Internal routine used to find client by ID and if not found this creates
991 new client entry and returns it. */
993 SilcClientEntry silc_client_get_client(SilcClient client,
994 SilcClientConnection conn,
995 SilcClientID *client_id)
997 SilcClientEntry client_entry;
999 client_entry = silc_client_get_client_by_id(client, conn, client_id);
1000 if (!client_entry) {
1001 client_entry = silc_client_add_client(client, conn, NULL, NULL, NULL,
1005 silc_client_ref_client(client, conn, client_entry);
1008 return client_entry;
1013 void silc_client_lock_client(SilcClientEntry client_entry)
1015 silc_rwlock_rdlock(client_entry->internal.lock);
1020 void silc_client_unlock_client(SilcClientEntry client_entry)
1022 silc_rwlock_unlock(client_entry->internal.lock);
1025 /* Take reference of client entry */
1027 SilcClientEntry silc_client_ref_client(SilcClient client,
1028 SilcClientConnection conn,
1029 SilcClientEntry client_entry)
1031 silc_atomic_add_int8(&client_entry->internal.refcnt, 1);
1032 SILC_LOG_DEBUG(("Client %p refcnt %d->%d", client_entry,
1033 silc_atomic_get_int8(&client_entry->internal.refcnt) - 1,
1034 silc_atomic_get_int8(&client_entry->internal.refcnt)));
1035 return client_entry;
1038 /* Release reference of client entry */
1040 void silc_client_unref_client(SilcClient client, SilcClientConnection conn,
1041 SilcClientEntry client_entry)
1044 SILC_LOG_DEBUG(("Client %p refcnt %d->%d", client_entry,
1045 silc_atomic_get_int8(&client_entry->internal.refcnt),
1046 silc_atomic_get_int8(&client_entry->internal.refcnt) - 1));
1047 silc_client_del_client(client, conn, client_entry);
1051 /* Free client entry list */
1053 void silc_client_list_free(SilcClient client, SilcClientConnection conn,
1054 SilcDList client_list)
1056 SilcClientEntry client_entry;
1059 silc_dlist_start(client_list);
1060 while ((client_entry = silc_dlist_get(client_list)))
1061 silc_client_unref_client(client, conn, client_entry);
1063 silc_dlist_uninit(client_list);
1067 /* Formats the nickname of the client specified by the `client_entry'.
1068 If the format is specified by the application this will format the
1069 nickname and replace the old nickname in the client entry. If the
1070 format string is not specified then this function has no effect.
1071 Returns the client entry that was formatted. */
1073 SilcClientEntry silc_client_nickname_format(SilcClient client,
1074 SilcClientConnection conn,
1075 SilcClientEntry client_entry,
1079 char newnick[128 + 1];
1080 int i, off = 0, len;
1083 SilcClientEntry entry, unformatted = NULL;
1085 if (!client->internal->params->nickname_format[0])
1086 return client_entry;
1087 if (!client_entry->nickname[0])
1090 SILC_LOG_DEBUG(("Format nickname"));
1092 /* Get all clients with same nickname. Do not perform the formatting
1093 if there aren't any clients with same nickname unless the application
1094 is forcing us to do so. */
1095 clients = silc_client_get_clients_local_ext(client, conn,
1096 client_entry->nickname,
1100 if (silc_dlist_count(clients) == 1 &&
1101 !client->internal->params->nickname_force_format) {
1102 silc_client_list_free(client, conn, clients);
1103 return client_entry;
1108 while ((entry = silc_dlist_get(clients))) {
1109 if (entry->internal.valid && entry != client_entry)
1111 if (entry->internal.valid && entry != client_entry &&
1112 silc_utf8_strcasecmp(entry->nickname, client_entry->nickname)) {
1114 unformatted = entry;
1118 if (!len || freebase) {
1119 silc_client_list_free(client, conn, clients);
1120 return client_entry;
1123 /* If priority formatting, this client always gets unformatted nickname. */
1124 if (unformatted && priority)
1125 client_entry = unformatted;
1127 memset(newnick, 0, sizeof(newnick));
1128 cp = client->internal->params->nickname_format;
1138 if (!client_entry->nickname[0])
1140 len = strlen(client_entry->nickname);
1141 memcpy(&newnick[off], client_entry->nickname, len);
1145 /* Stripped hostname */
1146 if (!client_entry->hostname[0])
1148 len = strcspn(client_entry->hostname, ".");
1149 i = strcspn(client_entry->hostname, "-");
1152 memcpy(&newnick[off], client_entry->hostname, len);
1157 if (!client_entry->hostname[0])
1159 len = strlen(client_entry->hostname);
1160 memcpy(&newnick[off], client_entry->hostname, len);
1164 /* Ascending number */
1169 if (silc_dlist_count(clients) == 1)
1172 silc_dlist_start(clients);
1173 while ((entry = silc_dlist_get(clients))) {
1174 if (!silc_utf8_strncasecmp(entry->nickname, newnick, off))
1176 if (strlen(entry->nickname) <= off)
1178 num = atoi(&entry->nickname[off]);
1183 memset(tmp, 0, sizeof(tmp));
1184 silc_snprintf(tmp, sizeof(tmp) - 1, "%d", ++max);
1186 memcpy(&newnick[off], tmp, len);
1191 /* Some other character in the string */
1192 memcpy(&newnick[off], cp, 1);
1201 memcpy(client_entry->nickname, newnick, strlen(newnick));
1202 silc_client_list_free(client, conn, clients);
1204 return client_entry;
1207 /* Parses nickname according to nickname format string */
1209 SilcBool silc_client_nickname_parse(SilcClient client,
1210 SilcClientConnection conn,
1214 char *cp, s = 0, e = 0, *nick;
1218 if (!client->internal->params->nickname_format[0]) {
1223 if (!nickname || !nickname[0])
1226 cp = client->internal->params->nickname_format;
1244 /* Get separator character */
1257 /* Parse the nickname */
1261 if (strchr(nickname, s))
1262 nick = strchr(nickname, s) + 1;
1264 if (strchr(nick, e))
1265 len = strchr(nick, e) - nick;
1269 *ret_nick = silc_memdup(nick, len);
1273 SILC_LOG_DEBUG(("Parsed nickname: %s", *ret_nick));
1278 /************************ Channel Searching Locally *************************/
1280 /* Finds entry for channel by the channel name. Returns the entry or NULL
1281 if the entry was not found. It is found only if the client is joined
1284 SilcChannelEntry silc_client_get_channel(SilcClient client,
1285 SilcClientConnection conn,
1288 SilcIDCacheEntry id_cache;
1289 SilcChannelEntry entry;
1291 if (!client || !conn || !channel)
1294 SILC_LOG_DEBUG(("Find channel %s", channel));
1296 /* Normalize name for search */
1297 channel = silc_channel_name_check(channel, strlen(channel), SILC_STRING_UTF8,
1302 silc_mutex_lock(conn->internal->lock);
1304 if (!silc_idcache_find_by_name_one(conn->internal->channel_cache, channel,
1306 silc_mutex_unlock(conn->internal->lock);
1311 SILC_LOG_DEBUG(("Found"));
1313 entry = id_cache->context;
1316 silc_client_ref_channel(client, conn, entry);
1317 silc_mutex_unlock(conn->internal->lock);
1324 /* Finds entry for channel by the channel ID. Returns the entry or NULL
1325 if the entry was not found. It is found only if the client is joined
1328 SilcChannelEntry silc_client_get_channel_by_id(SilcClient client,
1329 SilcClientConnection conn,
1330 SilcChannelID *channel_id)
1332 SilcIDCacheEntry id_cache;
1333 SilcChannelEntry entry;
1335 if (!client || !conn || !channel_id)
1338 SILC_LOG_DEBUG(("Find channel by id %s",
1339 silc_id_render(channel_id, SILC_ID_CHANNEL)));
1341 silc_mutex_lock(conn->internal->lock);
1343 if (!silc_idcache_find_by_id_one(conn->internal->channel_cache, channel_id,
1345 silc_mutex_unlock(conn->internal->lock);
1349 SILC_LOG_DEBUG(("Found"));
1351 entry = id_cache->context;
1354 silc_client_ref_channel(client, conn, entry);
1355 silc_mutex_unlock(conn->internal->lock);
1360 /********************** Channel Resolving from Server ***********************/
1362 /* Channel resolving context */
1365 SilcGetChannelCallback completion;
1367 } *SilcClientGetChannelInternal;
1369 /* Resolving command callback */
1371 static SilcBool silc_client_get_channel_cb(SilcClient client,
1372 SilcClientConnection conn,
1373 SilcCommand command,
1379 SilcClientGetChannelInternal i = context;
1380 SilcChannelEntry entry;
1382 if (error != SILC_STATUS_OK) {
1383 SILC_LOG_DEBUG(("Resolving failed: %s", silc_get_status_message(error)));
1385 i->completion(client, conn, error, NULL, i->context);
1389 /* Add the returned channel to list */
1390 if (i->completion) {
1391 entry = va_arg(ap, SilcChannelEntry);
1392 silc_client_ref_channel(client, conn, entry);
1393 silc_dlist_add(i->channels, entry);
1396 if (status == SILC_STATUS_OK || status == SILC_STATUS_LIST_END) {
1397 /* Deliver the channels to the caller */
1398 if (i->completion) {
1399 SILC_LOG_DEBUG(("Resolved %d channels", silc_dlist_count(i->channels)));
1400 silc_dlist_start(i->channels);
1401 i->completion(client, conn, SILC_STATUS_OK, i->channels, i->context);
1409 silc_client_list_free_channels(client, conn, i->channels);
1414 /* Resolves channel entry from the server by the channel name. */
1416 void silc_client_get_channel_resolve(SilcClient client,
1417 SilcClientConnection conn,
1419 SilcGetChannelCallback completion,
1422 SilcClientGetChannelInternal i;
1424 if (!client || !conn || !channel_name || !completion)
1427 SILC_LOG_DEBUG(("Resolve channel %s", channel_name));
1429 i = silc_calloc(1, sizeof(*i));
1432 i->completion = completion;
1433 i->context = context;
1434 i->channels = silc_dlist_init();
1440 /* Send the command */
1441 if (!silc_client_command_send(client, conn, SILC_COMMAND_IDENTIFY,
1442 silc_client_get_channel_cb, i, 1,
1443 3, channel_name, strlen(channel_name))) {
1445 completion(client, conn, SILC_STATUS_ERR_RESOURCE_LIMIT, NULL, context);
1449 /* Resolves channel information from the server by the channel ID. */
1452 silc_client_get_channel_by_id_resolve(SilcClient client,
1453 SilcClientConnection conn,
1454 SilcChannelID *channel_id,
1455 SilcGetChannelCallback completion,
1458 SilcClientGetChannelInternal i;
1460 SilcUInt16 cmd_ident;
1462 if (!client || !conn || !channel_id || !completion)
1465 SILC_LOG_DEBUG(("Resolve channel by id %s",
1466 silc_id_render(channel_id, SILC_ID_CHANNEL)));
1468 i = silc_calloc(1, sizeof(*i));
1471 i->completion = completion;
1472 i->context = context;
1473 i->channels = silc_dlist_init();
1479 /* Send the command */
1480 idp = silc_id_payload_encode(channel_id, SILC_ID_CHANNEL);
1481 cmd_ident = silc_client_command_send(client, conn, SILC_COMMAND_IDENTIFY,
1482 silc_client_get_channel_cb, i, 1,
1483 5, silc_buffer_datalen(idp));
1484 silc_buffer_free(idp);
1485 if (!cmd_ident && completion)
1486 completion(client, conn, SILC_STATUS_ERR_RESOURCE_LIMIT, NULL, context);
1491 /************************* Channel Entry Routines ***************************/
1493 /* Add new channel entry to the ID Cache */
1495 SilcChannelEntry silc_client_add_channel(SilcClient client,
1496 SilcClientConnection conn,
1497 const char *channel_name,
1499 SilcChannelID *channel_id)
1501 SilcChannelEntry channel;
1502 char *channel_namec;
1504 SILC_LOG_DEBUG(("Start"));
1506 channel = silc_calloc(1, sizeof(*channel));
1510 silc_rwlock_alloc(&channel->internal.lock);
1511 silc_atomic_init16(&channel->internal.refcnt, 0);
1512 channel->id = *channel_id;
1513 channel->mode = mode;
1515 channel->channel_name = strdup(channel_name);
1516 if (!channel->channel_name) {
1521 channel->user_list = silc_hash_table_alloc(1, silc_hash_ptr, NULL, NULL,
1522 NULL, NULL, NULL, TRUE);
1523 if (!channel->user_list) {
1524 silc_free(channel->channel_name);
1529 /* Normalize channel name */
1530 channel_namec = silc_channel_name_check(channel_name, strlen(channel_name),
1531 SILC_STRING_UTF8, 256, NULL);
1532 if (!channel_namec) {
1533 silc_free(channel->channel_name);
1534 silc_hash_table_free(channel->user_list);
1539 silc_mutex_lock(conn->internal->lock);
1541 /* Add channel to cache, the normalized channel name is saved to cache */
1542 if (!silc_idcache_add(conn->internal->channel_cache, channel_namec,
1543 &channel->id, channel)) {
1544 silc_free(channel_namec);
1545 silc_free(channel->channel_name);
1546 silc_hash_table_free(channel->user_list);
1548 silc_mutex_unlock(conn->internal->lock);
1552 silc_mutex_unlock(conn->internal->lock);
1553 silc_client_ref_channel(client, conn, channel);
1555 SILC_LOG_DEBUG(("Added %p", channel));
1560 /* Removes channel from the cache by the channel entry. */
1562 SilcBool silc_client_del_channel(SilcClient client, SilcClientConnection conn,
1563 SilcChannelEntry channel)
1565 SilcIDCacheEntry id_cache;
1573 if (silc_atomic_sub_int16(&channel->internal.refcnt, 1) > 0)
1576 SILC_LOG_DEBUG(("Deleting channel %p", channel));
1578 silc_mutex_lock(conn->internal->lock);
1579 if (silc_idcache_find_by_context(conn->internal->channel_cache, channel,
1581 silc_free(id_cache->name);
1582 ret = silc_idcache_del_by_context(conn->internal->channel_cache,
1584 silc_mutex_unlock(conn->internal->lock);
1589 silc_client_empty_channel(client, conn, channel);
1590 silc_hash_table_free(channel->user_list);
1591 silc_free(channel->channel_name);
1592 silc_free(channel->topic);
1593 if (channel->founder_key)
1594 silc_pkcs_public_key_free(channel->founder_key);
1595 if (channel->internal.send_key)
1596 silc_cipher_free(channel->internal.send_key);
1597 if (channel->internal.receive_key)
1598 silc_cipher_free(channel->internal.receive_key);
1599 if (channel->internal.hmac)
1600 silc_hmac_free(channel->internal.hmac);
1601 if (channel->internal.old_channel_keys) {
1602 silc_dlist_start(channel->internal.old_channel_keys);
1603 while ((key = silc_dlist_get(channel->internal.old_channel_keys)))
1604 silc_cipher_free(key);
1605 silc_dlist_uninit(channel->internal.old_channel_keys);
1607 if (channel->internal.old_hmacs) {
1608 silc_dlist_start(channel->internal.old_hmacs);
1609 while ((hmac = silc_dlist_get(channel->internal.old_hmacs)))
1610 silc_hmac_free(hmac);
1611 silc_dlist_uninit(channel->internal.old_hmacs);
1613 if (channel->channel_pubkeys)
1614 silc_argument_list_free(channel->channel_pubkeys,
1615 SILC_ARGUMENT_PUBLIC_KEY);
1616 silc_client_del_channel_private_keys(client, conn, channel);
1617 silc_atomic_uninit16(&channel->internal.refcnt);
1618 silc_rwlock_free(channel->internal.lock);
1619 silc_schedule_task_del_by_context(conn->client->schedule, channel);
1625 /* Replaces the channel ID of the `channel' to `new_id'. Returns FALSE
1626 if the ID could not be changed. This handles entry locking internally. */
1628 SilcBool silc_client_replace_channel_id(SilcClient client,
1629 SilcClientConnection conn,
1630 SilcChannelEntry channel,
1631 SilcChannelID *new_id)
1633 SilcBool ret = FALSE;
1638 SILC_LOG_DEBUG(("Old Channel ID id(%s)",
1639 silc_id_render(&channel->id, SILC_ID_CHANNEL)));
1640 SILC_LOG_DEBUG(("New Channel ID id(%s)",
1641 silc_id_render(new_id, SILC_ID_CHANNEL)));
1644 silc_rwlock_wrlock(channel->internal.lock);
1645 silc_mutex_lock(conn->internal->lock);
1646 silc_idcache_update_by_context(conn->internal->channel_cache, channel,
1647 new_id, NULL, FALSE);
1648 silc_mutex_unlock(conn->internal->lock);
1649 silc_rwlock_unlock(channel->internal.lock);
1656 void silc_client_lock_channel(SilcChannelEntry channel_entry)
1658 silc_rwlock_rdlock(channel_entry->internal.lock);
1661 /* Unlock channel */
1663 void silc_client_unlock_channel(SilcChannelEntry channel_entry)
1665 silc_rwlock_unlock(channel_entry->internal.lock);
1668 /* Take reference of channel entry */
1670 SilcChannelEntry silc_client_ref_channel(SilcClient client,
1671 SilcClientConnection conn,
1672 SilcChannelEntry channel_entry)
1674 silc_atomic_add_int16(&channel_entry->internal.refcnt, 1);
1675 SILC_LOG_DEBUG(("Channel %p refcnt %d->%d", channel_entry,
1676 silc_atomic_get_int16(&channel_entry->internal.refcnt) - 1,
1677 silc_atomic_get_int16(&channel_entry->internal.refcnt)));
1678 return channel_entry;
1681 /* Release reference of channel entry */
1683 void silc_client_unref_channel(SilcClient client, SilcClientConnection conn,
1684 SilcChannelEntry channel_entry)
1686 if (channel_entry) {
1687 SILC_LOG_DEBUG(("Channel %p refcnt %d->%d", channel_entry,
1688 silc_atomic_get_int16(&channel_entry->internal.refcnt),
1689 silc_atomic_get_int16(&channel_entry->internal.refcnt)
1691 silc_client_del_channel(client, conn, channel_entry);
1695 /* Free channel entry list */
1697 void silc_client_list_free_channels(SilcClient client,
1698 SilcClientConnection conn,
1699 SilcDList channel_list)
1701 SilcChannelEntry channel_entry;
1704 silc_dlist_start(channel_list);
1705 while ((channel_entry = silc_dlist_get(channel_list)))
1706 silc_client_unref_channel(client, conn, channel_entry);
1708 silc_dlist_uninit(channel_list);
1712 /************************* Server Searching Locally *************************/
1714 /* Finds entry for server by the server name. */
1716 SilcServerEntry silc_client_get_server(SilcClient client,
1717 SilcClientConnection conn,
1720 SilcIDCacheEntry id_cache;
1721 SilcServerEntry entry;
1723 if (!client || !conn || !server_name)
1726 SILC_LOG_DEBUG(("Find server by name %s", server_name));
1728 /* Normalize server name for search */
1729 server_name = silc_identifier_check(server_name, strlen(server_name),
1730 SILC_STRING_UTF8, 256, NULL);
1734 silc_mutex_lock(conn->internal->lock);
1736 if (!silc_idcache_find_by_name_one(conn->internal->server_cache,
1737 server_name, &id_cache)) {
1738 silc_free(server_name);
1739 silc_mutex_unlock(conn->internal->lock);
1743 SILC_LOG_DEBUG(("Found"));
1746 entry = id_cache->context;
1747 silc_client_ref_server(client, conn, entry);
1749 silc_mutex_unlock(conn->internal->lock);
1751 silc_free(server_name);
1756 /* Finds entry for server by the server ID. */
1758 SilcServerEntry silc_client_get_server_by_id(SilcClient client,
1759 SilcClientConnection conn,
1760 SilcServerID *server_id)
1762 SilcIDCacheEntry id_cache;
1763 SilcServerEntry entry;
1765 if (!client || !conn || !server_id)
1768 SILC_LOG_DEBUG(("Find server by id %s",
1769 silc_id_render(server_id, SILC_ID_SERVER)));
1771 silc_mutex_lock(conn->internal->lock);
1773 if (!silc_idcache_find_by_id_one(conn->internal->server_cache,
1774 server_id, &id_cache)) {
1775 silc_mutex_unlock(conn->internal->lock);
1779 SILC_LOG_DEBUG(("Found"));
1782 entry = id_cache->context;
1783 silc_client_ref_server(client, conn, entry);
1785 silc_mutex_unlock(conn->internal->lock);
1790 /*********************** Server Resolving from Server ***********************/
1792 /* Resolving context */
1795 SilcGetServerCallback completion;
1797 } *SilcClientGetServerInternal;
1799 /* Resolving command callback */
1801 static SilcBool silc_client_get_server_cb(SilcClient client,
1802 SilcClientConnection conn,
1803 SilcCommand command,
1809 SilcClientGetServerInternal i = context;
1810 SilcServerEntry server;
1812 if (error != SILC_STATUS_OK) {
1813 SILC_LOG_DEBUG(("Resolving failed: %s", silc_get_status_message(error)));
1815 i->completion(client, conn, error, NULL, i->context);
1819 /* Add the returned servers to list */
1820 if (i->completion) {
1821 server = va_arg(ap, SilcServerEntry);
1822 silc_client_ref_server(client, conn, server);
1823 silc_dlist_add(i->servers, server);
1824 server->internal.resolve_cmd_ident = 0;
1827 if (status == SILC_STATUS_OK || status == SILC_STATUS_LIST_END) {
1828 /* Deliver the servers to the caller */
1829 if (i->completion) {
1830 SILC_LOG_DEBUG(("Resolved %d servers", silc_dlist_count(i->servers)));
1831 silc_dlist_start(i->servers);
1832 i->completion(client, conn, SILC_STATUS_OK, i->servers, i->context);
1840 silc_client_list_free_servers(client, conn, i->servers);
1845 /* Resolve server by server ID */
1848 silc_client_get_server_by_id_resolve(SilcClient client,
1849 SilcClientConnection conn,
1850 SilcServerID *server_id,
1851 SilcGetServerCallback completion,
1854 SilcClientGetServerInternal i;
1855 SilcServerEntry server;
1857 SilcUInt16 cmd_ident;
1859 if (!client || !conn || !server_id || !completion)
1862 SILC_LOG_DEBUG(("Resolve server by id %s",
1863 silc_id_render(server_id, SILC_ID_SERVER)));
1865 i = silc_calloc(1, sizeof(*i));
1868 i->completion = completion;
1869 i->context = context;
1870 i->servers = silc_dlist_init();
1876 /* Attach to resolving, if on going */
1877 server = silc_client_get_server_by_id(client, conn, server_id);
1878 if (server && server->internal.resolve_cmd_ident) {
1879 SILC_LOG_DEBUG(("Attach to existing resolving"));
1880 silc_client_unref_server(client, conn, server);
1881 silc_client_command_pending(conn, SILC_COMMAND_NONE,
1882 server->internal.resolve_cmd_ident,
1883 silc_client_get_server_cb, i);
1884 return server->internal.resolve_cmd_ident;
1887 /* Send the command */
1888 idp = silc_id_payload_encode(server_id, SILC_ID_SERVER);
1889 cmd_ident = silc_client_command_send(client, conn, SILC_COMMAND_IDENTIFY,
1890 silc_client_get_server_cb, i, 1,
1891 5, silc_buffer_datalen(idp));
1892 silc_buffer_free(idp);
1893 if (!cmd_ident && completion)
1894 completion(client, conn, SILC_STATUS_ERR_RESOURCE_LIMIT, NULL, context);
1896 if (server && cmd_ident)
1897 server->internal.resolve_cmd_ident = cmd_ident;
1899 silc_client_unref_server(client, conn, server);
1904 /************************** Server Entry Routines ***************************/
1906 /* Add new server entry */
1908 SilcServerEntry silc_client_add_server(SilcClient client,
1909 SilcClientConnection conn,
1910 const char *server_name,
1911 const char *server_info,
1912 SilcServerID *server_id)
1914 SilcServerEntry server_entry;
1915 char *server_namec = NULL;
1920 SILC_LOG_DEBUG(("Adding new server %s", server_name));
1922 server_entry = silc_calloc(1, sizeof(*server_entry));
1926 silc_rwlock_alloc(&server_entry->internal.lock);
1927 silc_atomic_init8(&server_entry->internal.refcnt, 0);
1928 server_entry->id = *server_id;
1930 server_entry->server_name = strdup(server_name);
1932 server_entry->server_info = strdup(server_info);
1934 /* Normalize server name */
1936 server_namec = silc_identifier_check(server_name, strlen(server_name),
1937 SILC_STRING_UTF8, 256, NULL);
1938 if (!server_namec) {
1939 silc_free(server_entry->server_name);
1940 silc_free(server_entry->server_info);
1941 silc_free(server_entry);
1946 silc_mutex_lock(conn->internal->lock);
1948 /* Add server to cache */
1949 if (!silc_idcache_add(conn->internal->server_cache, server_namec,
1950 &server_entry->id, server_entry)) {
1951 silc_free(server_namec);
1952 silc_free(server_entry->server_name);
1953 silc_free(server_entry->server_info);
1954 silc_free(server_entry);
1955 silc_mutex_unlock(conn->internal->lock);
1959 silc_mutex_unlock(conn->internal->lock);
1960 silc_client_ref_server(client, conn, server_entry);
1962 SILC_LOG_DEBUG(("Added %p", server_entry));
1964 return server_entry;
1967 /* Removes server from the cache by the server entry. */
1969 SilcBool silc_client_del_server(SilcClient client, SilcClientConnection conn,
1970 SilcServerEntry server)
1972 SilcIDCacheEntry id_cache;
1978 if (silc_atomic_sub_int8(&server->internal.refcnt, 1) > 0)
1981 SILC_LOG_DEBUG(("Deleting server %p", server));
1983 silc_mutex_lock(conn->internal->lock);
1984 if (silc_idcache_find_by_context(conn->internal->server_cache, server,
1986 silc_free(id_cache->name);
1987 ret = silc_idcache_del_by_context(conn->internal->server_cache,
1989 silc_mutex_unlock(conn->internal->lock);
1991 silc_free(server->server_name);
1992 silc_free(server->server_info);
1993 if (server->public_key)
1994 silc_pkcs_public_key_free(server->public_key);
1995 silc_atomic_uninit8(&server->internal.refcnt);
1996 silc_rwlock_free(server->internal.lock);
2002 /* Updates the `server_entry' with the new information sent as argument. */
2004 void silc_client_update_server(SilcClient client,
2005 SilcClientConnection conn,
2006 SilcServerEntry server_entry,
2007 const char *server_name,
2008 const char *server_info)
2010 char *server_namec = NULL;
2012 SILC_LOG_DEBUG(("Updating server %p", server_entry));
2015 (!server_entry->server_name ||
2016 !silc_utf8_strcasecmp(server_entry->server_name, server_name))) {
2018 server_namec = silc_identifier_check(server_name, strlen(server_name),
2019 SILC_STRING_UTF8, 256, NULL);
2023 silc_free(server_entry->server_name);
2024 server_entry->server_name = strdup(server_name);
2025 if (!server_entry->server_name)
2028 /* Update cache entry */
2029 silc_mutex_lock(conn->internal->lock);
2030 silc_idcache_update_by_context(conn->internal->server_cache, server_entry,
2031 NULL, server_namec, TRUE);
2032 silc_mutex_unlock(conn->internal->lock);
2036 (!server_entry->server_info ||
2037 !silc_utf8_strcasecmp(server_entry->server_info, server_info))) {
2038 silc_free(server_entry->server_info);
2039 server_entry->server_info = strdup(server_info);
2045 void silc_client_lock_server(SilcServerEntry server_entry)
2047 silc_rwlock_rdlock(server_entry->internal.lock);
2052 void silc_client_unlock_server(SilcServerEntry server_entry)
2054 silc_rwlock_unlock(server_entry->internal.lock);
2057 /* Take reference of server entry */
2059 SilcServerEntry silc_client_ref_server(SilcClient client,
2060 SilcClientConnection conn,
2061 SilcServerEntry server_entry)
2063 silc_atomic_add_int8(&server_entry->internal.refcnt, 1);
2064 SILC_LOG_DEBUG(("Server %p refcnt %d->%d", server_entry,
2065 silc_atomic_get_int8(&server_entry->internal.refcnt) - 1,
2066 silc_atomic_get_int8(&server_entry->internal.refcnt)));
2067 return server_entry;
2070 /* Release reference of server entry */
2072 void silc_client_unref_server(SilcClient client, SilcClientConnection conn,
2073 SilcServerEntry server_entry)
2076 SILC_LOG_DEBUG(("Server %p refcnt %d->%d", server_entry,
2077 silc_atomic_get_int8(&server_entry->internal.refcnt),
2078 silc_atomic_get_int8(&server_entry->internal.refcnt)
2080 silc_client_del_server(client, conn, server_entry);
2084 /* Free server entry list */
2086 void silc_client_list_free_servers(SilcClient client,
2087 SilcClientConnection conn,
2088 SilcDList server_list)
2090 SilcServerEntry server_entry;
2093 silc_dlist_start(server_list);
2094 while ((server_entry = silc_dlist_get(server_list)))
2095 silc_client_unref_server(client, conn, server_entry);
2097 silc_dlist_uninit(server_list);