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 in case 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 nick[128 + 1], serv[256 + 1], userhost[768 + 1], *parsed = NULL;
326 SILC_LOG_DEBUG(("Resolve client by %s command",
327 silc_get_command_name(command)));
329 if (!client || !conn)
331 if (!nickname && !attributes)
334 /* Parse server name from the nickname if set */
335 if (silc_parse_userfqdn(nickname, nick, sizeof(nick),
336 serv, sizeof(serv) == 2))
337 server = (const char *)serv;
338 nickname = (const char *)nick;
340 /* Parse nickname in case it is formatted */
341 if (silc_client_nickname_parse(client, conn, (char *)nickname, &parsed))
342 nickname = (const char *)parsed;
344 i = silc_calloc(1, sizeof(*i));
349 i->clients = silc_dlist_init();
355 i->completion = completion;
356 i->context = context;
358 memset(userhost, 0, sizeof(userhost));
359 if (nickname && server) {
360 len = strlen(nickname) + strlen(server) + 3;
361 silc_strncat(userhost, len, nickname, strlen(nickname));
362 silc_strncat(userhost, len, "@", 1);
363 silc_strncat(userhost, len, server, strlen(server));
364 } else if (nickname) {
365 silc_strncat(userhost, sizeof(userhost) - 1, nickname, strlen(nickname));
369 /* Send the command */
370 if (command == SILC_COMMAND_IDENTIFY)
371 return silc_client_command_send(client, conn, SILC_COMMAND_IDENTIFY,
372 silc_client_get_clients_cb, i,
373 1, 1, userhost, strlen(userhost));
374 return silc_client_command_send(client, conn, SILC_COMMAND_WHOIS,
375 silc_client_get_clients_cb, i,
376 2, 1, userhost, strlen(userhost),
377 3, silc_buffer_datalen(attributes));
380 /* Get clients from server with IDENTIFY command */
382 SilcUInt16 silc_client_get_clients(SilcClient client,
383 SilcClientConnection conn,
384 const char *nickname,
386 SilcGetClientCallback completion,
389 return silc_client_get_clients_i(client, conn, SILC_COMMAND_IDENTIFY,
390 nickname, server, NULL,
391 completion, context);
394 /* Get clients from server with WHOIS command */
396 SilcUInt16 silc_client_get_clients_whois(SilcClient client,
397 SilcClientConnection conn,
398 const char *nickname,
400 SilcBuffer attributes,
401 SilcGetClientCallback completion,
404 return silc_client_get_clients_i(client, conn, SILC_COMMAND_WHOIS,
405 nickname, server, attributes,
406 completion, context);
409 /* ID list resolving context */
411 SilcGetClientCallback completion;
413 SilcBuffer client_id_list;
414 SilcUInt32 list_count;
415 } *GetClientsByListInternal;
417 static SilcBool silc_client_get_clients_list_cb(SilcClient client,
418 SilcClientConnection conn,
425 GetClientsByListInternal i = context;
426 SilcClientEntry client_entry;
432 /* Process the list after all replies have been received */
433 if (status != SILC_STATUS_OK && !SILC_STATUS_IS_ERROR(status) &&
434 status != SILC_STATUS_LIST_END)
437 SILC_LOG_DEBUG(("Resolved all clients"));
439 clients = silc_dlist_init();
441 status = SILC_STATUS_ERR_RESOURCE_LIMIT;
445 for (c = 0; c < i->list_count; c++) {
447 SILC_GET16_MSB(idp_len, i->client_id_list->data + 2);
449 if (!silc_id_payload_parse_id(i->client_id_list->data, idp_len, &id)) {
450 status = SILC_STATUS_ERR_BAD_CLIENT_ID;
454 /* Get client entry */
455 client_entry = silc_client_get_client_by_id(client, conn, &id.u.client_id);
457 silc_dlist_add(clients, client_entry);
459 if (!silc_buffer_pull(i->client_id_list, idp_len)) {
460 status = SILC_STATUS_ERR_BAD_CLIENT_ID;
465 silc_dlist_start(clients);
466 status = SILC_STATUS_OK;
468 i->completion(client, conn, status, clients, i->context);
471 if (status != SILC_STATUS_OK && i->completion)
472 i->completion(client, conn, status, NULL, i->context);
474 silc_client_list_free(client, conn, clients);
475 silc_buffer_free(i->client_id_list);
481 /* Gets client entries by the list of client ID's `client_id_list'. This
482 always resolves those client ID's it does not know yet from the server
483 so this function might take a while. The `client_id_list' is a list
484 of ID Payloads added one after other. JOIN command reply and USERS
485 command reply for example returns this sort of list. The `completion'
486 will be called after the entries are available. */
488 SilcUInt16 silc_client_get_clients_by_list(SilcClient client,
489 SilcClientConnection conn,
490 SilcUInt32 list_count,
491 SilcBuffer client_id_list,
492 SilcGetClientCallback completion,
495 GetClientsByListInternal in;
496 SilcClientEntry entry;
497 unsigned char **res_argv = NULL;
498 SilcUInt32 *res_argv_lens = NULL, *res_argv_types = NULL, res_argc = 0;
499 SilcUInt16 idp_len, cmd_ident;
504 SILC_LOG_DEBUG(("Resolve clients from Client ID list"));
506 if (!client || !conn || !client_id_list)
509 in = silc_calloc(1, sizeof(*in));
512 in->completion = completion;
513 in->context = context;
514 in->list_count = list_count;
515 in->client_id_list = silc_buffer_copy(client_id_list);
516 if (!in->client_id_list)
519 for (i = 0; i < list_count; i++) {
521 SILC_GET16_MSB(idp_len, client_id_list->data + 2);
523 if (!silc_id_payload_parse_id(client_id_list->data, idp_len, &id))
526 /* Check if we have this client cached already. If we don't have the
527 entry or it has incomplete info, then resolve it from the server. */
528 entry = silc_client_get_client_by_id(client, conn, &id.u.client_id);
529 if (!entry || !entry->nickname[0] || !entry->username[0] ||
532 res_argv = silc_calloc(list_count, sizeof(*res_argv));
533 res_argv_lens = silc_calloc(list_count, sizeof(*res_argv_lens));
534 res_argv_types = silc_calloc(list_count, sizeof(*res_argv_types));
535 if (!res_argv || !res_argv_lens || !res_argv_types) {
536 silc_client_unref_client(client, conn, entry);
541 res_argv[res_argc] = client_id_list->data;
542 res_argv_lens[res_argc] = idp_len;
543 res_argv_types[res_argc] = res_argc + 4;
546 silc_client_unref_client(client, conn, entry);
548 if (!silc_buffer_pull(client_id_list, idp_len))
551 silc_buffer_start(client_id_list);
553 /* Query the unknown client information from server */
555 cmd_ident = silc_client_command_send_argv(client,
556 conn, SILC_COMMAND_WHOIS,
557 silc_client_get_clients_list_cb,
558 in, res_argc, res_argv,
562 silc_free(res_argv_lens);
563 silc_free(res_argv_types);
567 /* We have the clients in cache, get them and call the completion */
568 silc_client_get_clients_list_cb(client, conn, SILC_COMMAND_WHOIS,
569 SILC_STATUS_OK, SILC_STATUS_OK, in, tmp);
573 silc_buffer_free(in->client_id_list);
576 silc_free(res_argv_lens);
577 silc_free(res_argv_types);
584 SilcClientConnection conn;
585 SilcChannelID channel_id;
586 SilcGetClientCallback completion;
589 } *GetClientsByChannelInternal;
591 SILC_CLIENT_CMD_FUNC(get_clients_by_channel_cb)
593 GetClientsByChannelInternal i = context;
594 SilcClientEntry *clients = NULL;
595 SilcUInt32 clients_count = 0;
596 SilcBool found = FALSE;
597 SilcChannelEntry channel;
598 SilcHashTableList htl;
607 channel = silc_client_get_channel_by_id(i->client, i->conn, &i->channel_id);
608 if (channel && !silc_hash_table_count(channel->user_list)) {
609 clients = silc_calloc(silc_hash_table_count(channel->user_list),
611 silc_hash_table_list(channel->user_list, &htl);
612 while (silc_hash_table_get(&htl, NULL, (void *)&chu))
613 clients[clients_count++] = chu->client;
614 silc_hash_table_list_reset(&htl);
619 i->completion(i->client, i->conn, clients, clients_count, i->context);
622 i->completion(i->client, i->conn, NULL, 0, i->context);
628 /* Gets client entries by the channel entry indicated by `channel'. Thus,
629 it resolves the clients currently on that channel. */
631 void silc_client_get_clients_by_channel(SilcClient client,
632 SilcClientConnection conn,
633 SilcChannelEntry channel,
634 SilcGetClientCallback completion,
637 GetClientsByChannelInternal in;
638 SilcHashTableList htl;
640 SilcClientEntry entry;
641 unsigned char **res_argv = NULL;
642 SilcUInt32 *res_argv_lens = NULL, *res_argv_types = NULL, res_argc = 0;
644 SilcBool wait_res = FALSE;
646 assert(client && conn && channel);
648 SILC_LOG_DEBUG(("Start"));
650 in = silc_calloc(1, sizeof(*in));
653 in->channel_id = *channel->id;
654 in->completion = completion;
655 in->context = context;
657 /* If user list does not exist, send USERS command. */
658 if (!channel->user_list || !silc_hash_table_count(channel->user_list)) {
659 SILC_LOG_DEBUG(("Sending USERS"));
660 silc_client_command_register(client, SILC_COMMAND_USERS, NULL, NULL,
661 silc_client_command_reply_users_i, 0,
663 silc_client_command_send(client, conn, SILC_COMMAND_USERS,
664 conn->cmd_ident, 1, 2, channel->channel_name,
665 strlen(channel->channel_name));
666 silc_client_command_pending(conn, SILC_COMMAND_USERS, conn->cmd_ident,
667 silc_client_command_get_clients_by_channel_cb,
672 silc_hash_table_list(channel->user_list, &htl);
673 while (silc_hash_table_get(&htl, NULL, (void *)&chu)) {
676 /* If the entry has incomplete info, then resolve it from the server. */
677 if (!entry->nickname[0] || !entry->realname) {
678 if (entry->status & SILC_CLIENT_STATUS_RESOLVING) {
679 /* Attach to this resolving and wait until it finishes */
680 silc_client_command_pending(
681 conn, SILC_COMMAND_NONE,
682 entry->resolve_cmd_ident,
683 silc_client_command_get_clients_by_channel_cb,
689 entry->status |= SILC_CLIENT_STATUS_RESOLVING;
690 entry->resolve_cmd_ident = conn->cmd_ident + 1;
692 idp = silc_id_payload_encode(entry->id, SILC_ID_CLIENT);
694 /* No we don't have it, query it from the server. Assemble argument
695 table that will be sent for the WHOIS command later. */
696 res_argv = silc_realloc(res_argv, sizeof(*res_argv) *
698 res_argv_lens = silc_realloc(res_argv_lens, sizeof(*res_argv_lens) *
700 res_argv_types = silc_realloc(res_argv_types, sizeof(*res_argv_types) *
702 res_argv[res_argc] = silc_memdup(idp->data, idp->len);
703 res_argv_lens[res_argc] = idp->len;
704 res_argv_types[res_argc] = res_argc + 4;
707 silc_buffer_free(idp);
710 silc_hash_table_list_reset(&htl);
712 /* Query the client information from server if the list included clients
713 that we don't know about. */
717 /* Send the WHOIS command to server */
718 res_cmd = silc_command_payload_encode(SILC_COMMAND_WHOIS,
719 res_argc, res_argv, res_argv_lens,
720 res_argv_types, ++conn->cmd_ident);
721 silc_client_packet_send(client, conn->sock, SILC_PACKET_COMMAND,
722 NULL, 0, NULL, NULL, res_cmd->data, res_cmd->len,
725 /* Register our own command reply for this command */
726 silc_client_command_register(client, SILC_COMMAND_WHOIS, NULL, NULL,
727 silc_client_command_reply_whois_i, 0,
730 /* Process the applications request after reply has been received */
731 silc_client_command_pending(
732 conn, SILC_COMMAND_WHOIS, conn->cmd_ident,
733 silc_client_command_get_clients_by_channel_cb,
737 silc_buffer_free(res_cmd);
739 silc_free(res_argv_lens);
740 silc_free(res_argv_types);
747 /* We have the clients in cache, get them and call the completion */
748 silc_client_command_get_clients_by_channel_cb((void *)in, NULL);
753 /************************** Client Entry Routines ***************************/
755 /* Creates new client entry and adds it to the ID cache. Returns pointer
758 SilcClientEntry silc_client_add_client(SilcClient client,
759 SilcClientConnection conn,
760 char *nickname, char *username,
761 char *userinfo, SilcClientID *id,
764 SilcClientEntry client_entry;
767 SILC_LOG_DEBUG(("Adding new client entry"));
769 /* Save the client infos */
770 client_entry = silc_calloc(1, sizeof(*client_entry));
774 silc_rwlock_alloc(&client_entry->internal.lock);
775 silc_atomic_init8(&client_entry->internal.refcnt, 0);
776 client_entry->id = *id;
777 client_entry->mode = mode;
778 client_entry->realname = userinfo ? strdup(userinfo) : NULL;
779 silc_parse_userfqdn(nickname, client_entry->nickname,
780 sizeof(client_entry->nickname),
781 client_entry->server,
782 sizeof(client_entry->server));
783 silc_parse_userfqdn(username, client_entry->username,
784 sizeof(client_entry->username),
785 client_entry->hostname,
786 sizeof(client_entry->hostname));
787 client_entry->channels = silc_hash_table_alloc(1, silc_hash_ptr, NULL, NULL,
788 NULL, NULL, NULL, TRUE);
789 if (!client_entry->channels) {
790 silc_free(client_entry->realname);
791 silc_free(client_entry);
795 /* Normalize nickname */
796 if (client_entry->nickname[0]) {
797 nick = silc_identifier_check(client_entry->nickname,
798 strlen(client_entry->nickname),
799 SILC_STRING_UTF8, 128, NULL);
801 silc_free(client_entry->realname);
802 silc_hash_table_free(client_entry->channels);
803 silc_free(client_entry);
808 silc_mutex_lock(conn->internal->lock);
810 /* Add client to cache, the normalized nickname is saved to cache */
811 if (!silc_idcache_add(conn->internal->client_cache, nick,
812 &client_entry->id, client_entry)) {
814 silc_free(client_entry->realname);
815 silc_hash_table_free(client_entry->channels);
816 silc_free(client_entry);
817 silc_mutex_unlock(conn->internal->lock);
821 client_entry->nickname_normalized = nick;
823 silc_mutex_unlock(conn->internal->lock);
824 silc_client_ref_client(client, conn, client_entry);
826 /* Format the nickname */
827 silc_client_nickname_format(client, conn, client_entry, FALSE);
829 if (client_entry->nickname[0])
830 client_entry->internal.valid = TRUE;
832 SILC_LOG_DEBUG(("Added %p", client_entry));
837 /* Updates the `client_entry' with the new information sent as argument.
838 This handles entry locking internally. */
840 void silc_client_update_client(SilcClient client,
841 SilcClientConnection conn,
842 SilcClientEntry client_entry,
843 const char *nickname,
844 const char *username,
845 const char *userinfo,
850 SILC_LOG_DEBUG(("Update client entry"));
852 silc_rwlock_wrlock(client_entry->internal.lock);
854 if (!client_entry->realname && userinfo)
855 client_entry->realname = strdup(userinfo);
856 if ((!client_entry->username[0] || !client_entry->hostname[0]) && username)
857 silc_parse_userfqdn(username, client_entry->username,
858 sizeof(client_entry->username),
859 client_entry->hostname,
860 sizeof(client_entry->username));
861 if (!client_entry->nickname[0] && nickname) {
862 silc_parse_userfqdn(nickname, client_entry->nickname,
863 sizeof(client_entry->nickname),
864 client_entry->server,
865 sizeof(client_entry->server));
867 /* Normalize nickname */
868 nick = silc_identifier_check(client_entry->nickname,
869 strlen(client_entry->nickname),
870 SILC_STRING_UTF8, 128, NULL);
872 silc_rwlock_unlock(client_entry->internal.lock);
876 /* Format nickname */
877 silc_client_nickname_format(client, conn, client_entry,
878 client_entry == conn->local_entry);
880 /* Update cache entry */
881 silc_mutex_lock(conn->internal->lock);
882 silc_idcache_update_by_context(conn->internal->client_cache,
883 client_entry, NULL, nick, TRUE);
884 silc_mutex_unlock(conn->internal->lock);
885 client_entry->nickname_normalized = nick;
886 client_entry->internal.valid = TRUE;
888 client_entry->mode = mode;
890 silc_rwlock_unlock(client_entry->internal.lock);
893 /* Change a client's nickname. Must be called with `client_entry' locked. */
895 SilcBool silc_client_change_nickname(SilcClient client,
896 SilcClientConnection conn,
897 SilcClientEntry client_entry,
898 const char *new_nick,
899 SilcClientID *new_id,
900 const unsigned char *idp,
905 SILC_LOG_DEBUG(("Change nickname %s to %s", client_entry->nickname,
908 /* Normalize nickname */
909 tmp = silc_identifier_check(new_nick, strlen(new_nick),
910 SILC_STRING_UTF8, 128, NULL);
914 /* Update the client entry */
915 silc_mutex_lock(conn->internal->lock);
916 if (!silc_idcache_update_by_context(conn->internal->client_cache,
917 client_entry, new_id, tmp, TRUE)) {
919 silc_mutex_unlock(conn->internal->lock);
922 silc_mutex_unlock(conn->internal->lock);
924 memset(client_entry->nickname, 0, sizeof(client_entry->nickname));
925 memcpy(client_entry->nickname, new_nick, strlen(new_nick));
926 client_entry->nickname_normalized = tmp;
927 silc_client_nickname_format(client, conn, client_entry,
928 client_entry == conn->local_entry);
930 /* For my client entry, update ID and set new ID to packet stream */
931 if (client_entry == conn->local_entry) {
932 if (idp && idp_len) {
933 silc_buffer_enlarge(conn->internal->local_idp, idp_len);
934 silc_buffer_put(conn->internal->local_idp, idp, idp_len);
937 silc_packet_set_ids(conn->stream, SILC_ID_CLIENT, conn->local_id,
941 client_entry->internal.valid = TRUE;
945 /* Deletes the client entry and frees all memory. */
947 void silc_client_del_client_entry(SilcClient client,
948 SilcClientConnection conn,
949 SilcClientEntry client_entry)
951 silc_free(client_entry->realname);
952 silc_free(client_entry->nickname_normalized);
953 silc_free(client_entry->internal.key);
954 if (client_entry->public_key)
955 silc_pkcs_public_key_free(client_entry->public_key);
956 silc_hash_table_free(client_entry->channels);
957 if (client_entry->internal.send_key)
958 silc_cipher_free(client_entry->internal.send_key);
959 if (client_entry->internal.receive_key)
960 silc_cipher_free(client_entry->internal.receive_key);
961 if (client_entry->internal.hmac_send)
962 silc_hmac_free(client_entry->internal.hmac_send);
963 if (client_entry->internal.hmac_receive)
964 silc_hmac_free(client_entry->internal.hmac_receive);
965 silc_client_ftp_session_free_client(client, client_entry);
966 if (client_entry->internal.ke)
967 silc_client_abort_key_agreement(client, conn, client_entry);
968 silc_atomic_uninit8(&client_entry->internal.refcnt);
969 silc_rwlock_free(client_entry->internal.lock);
970 silc_free(client_entry);
973 /* Removes client from the cache by the client entry. */
975 SilcBool silc_client_del_client(SilcClient client, SilcClientConnection conn,
976 SilcClientEntry client_entry)
983 if (silc_atomic_sub_int8(&client_entry->internal.refcnt, 1) > 0)
986 SILC_LOG_DEBUG(("Deleting client %p", client_entry));
988 silc_mutex_lock(conn->internal->lock);
989 ret = silc_idcache_del_by_context(conn->internal->client_cache,
991 silc_mutex_unlock(conn->internal->lock);
994 /* Remove from channels */
995 silc_client_remove_from_channels(client, conn, client_entry);
997 /* Free the client entry data */
998 silc_client_del_client_entry(client, conn, client_entry);
1004 /* Internal routine used to find client by ID and if not found this creates
1005 new client entry and returns it. */
1007 SilcClientEntry silc_client_get_client(SilcClient client,
1008 SilcClientConnection conn,
1009 SilcClientID *client_id)
1011 SilcClientEntry client_entry;
1013 client_entry = silc_client_get_client_by_id(client, conn, client_id);
1014 if (!client_entry) {
1015 client_entry = silc_client_add_client(client, conn, NULL, NULL, NULL,
1019 silc_client_ref_client(client, conn, client_entry);
1022 return client_entry;
1027 void silc_client_lock_client(SilcClientEntry client_entry)
1029 silc_rwlock_rdlock(client_entry->internal.lock);
1034 void silc_client_unlock_client(SilcClientEntry client_entry)
1036 silc_rwlock_unlock(client_entry->internal.lock);
1039 /* Take reference of client entry */
1041 SilcClientEntry silc_client_ref_client(SilcClient client,
1042 SilcClientConnection conn,
1043 SilcClientEntry client_entry)
1045 silc_atomic_add_int8(&client_entry->internal.refcnt, 1);
1046 SILC_LOG_DEBUG(("Client %p refcnt %d->%d", client_entry,
1047 silc_atomic_get_int8(&client_entry->internal.refcnt) - 1,
1048 silc_atomic_get_int8(&client_entry->internal.refcnt)));
1049 return client_entry;
1052 /* Release reference of client entry */
1054 void silc_client_unref_client(SilcClient client, SilcClientConnection conn,
1055 SilcClientEntry client_entry)
1058 SILC_LOG_DEBUG(("Client %p refcnt %d->%d", client_entry,
1059 silc_atomic_get_int8(&client_entry->internal.refcnt),
1060 silc_atomic_get_int8(&client_entry->internal.refcnt) - 1));
1061 silc_client_del_client(client, conn, client_entry);
1065 /* Free client entry list */
1067 void silc_client_list_free(SilcClient client, SilcClientConnection conn,
1068 SilcDList client_list)
1070 SilcClientEntry client_entry;
1073 silc_dlist_start(client_list);
1074 while ((client_entry = silc_dlist_get(client_list)))
1075 silc_client_unref_client(client, conn, client_entry);
1077 silc_dlist_uninit(client_list);
1081 /* Formats the nickname of the client specified by the `client_entry'.
1082 If the format is specified by the application this will format the
1083 nickname and replace the old nickname in the client entry. If the
1084 format string is not specified then this function has no effect.
1085 Returns the client entry that was formatted. */
1087 SilcClientEntry silc_client_nickname_format(SilcClient client,
1088 SilcClientConnection conn,
1089 SilcClientEntry client_entry,
1093 char newnick[128 + 1];
1094 int i, off = 0, len;
1097 SilcClientEntry entry, unformatted = NULL;
1099 if (!client->internal->params->nickname_format[0])
1100 return client_entry;
1101 if (!client_entry->nickname[0])
1104 SILC_LOG_DEBUG(("Format nickname"));
1106 /* Get all clients with same nickname. Do not perform the formatting
1107 if there aren't any clients with same nickname unless the application
1108 is forcing us to do so. */
1109 clients = silc_client_get_clients_local_ext(client, conn,
1110 client_entry->nickname,
1114 if (silc_dlist_count(clients) == 1 &&
1115 !client->internal->params->nickname_force_format) {
1116 silc_client_list_free(client, conn, clients);
1117 return client_entry;
1122 while ((entry = silc_dlist_get(clients))) {
1123 if (entry->internal.valid && entry != client_entry)
1125 if (entry->internal.valid && entry != client_entry &&
1126 silc_utf8_strcasecmp(entry->nickname, client_entry->nickname)) {
1128 unformatted = entry;
1132 if (!len || freebase) {
1133 silc_client_list_free(client, conn, clients);
1134 return client_entry;
1137 /* If priority formatting, this client always gets unformatted nickname. */
1138 if (unformatted && priority)
1139 client_entry = unformatted;
1141 memset(newnick, 0, sizeof(newnick));
1142 cp = client->internal->params->nickname_format;
1152 if (!client_entry->nickname[0])
1154 len = strlen(client_entry->nickname);
1155 memcpy(&newnick[off], client_entry->nickname, len);
1159 /* Stripped hostname */
1160 if (!client_entry->hostname[0])
1162 len = strcspn(client_entry->hostname, ".");
1163 i = strcspn(client_entry->hostname, "-");
1166 memcpy(&newnick[off], client_entry->hostname, len);
1171 if (!client_entry->hostname[0])
1173 len = strlen(client_entry->hostname);
1174 memcpy(&newnick[off], client_entry->hostname, len);
1178 /* Ascending number */
1183 if (silc_dlist_count(clients) == 1)
1186 silc_dlist_start(clients);
1187 while ((entry = silc_dlist_get(clients))) {
1188 if (!silc_utf8_strncasecmp(entry->nickname, newnick, off))
1190 if (strlen(entry->nickname) <= off)
1192 num = atoi(&entry->nickname[off]);
1197 memset(tmp, 0, sizeof(tmp));
1198 silc_snprintf(tmp, sizeof(tmp) - 1, "%d", ++max);
1200 memcpy(&newnick[off], tmp, len);
1205 /* Some other character in the string */
1206 memcpy(&newnick[off], cp, 1);
1215 memcpy(client_entry->nickname, newnick, strlen(newnick));
1216 silc_client_list_free(client, conn, clients);
1218 return client_entry;
1221 /* Parses nickname according to nickname format string */
1223 SilcBool silc_client_nickname_parse(SilcClient client,
1224 SilcClientConnection conn,
1228 char *cp, s = 0, e = 0, *nick;
1232 if (!client->internal->params->nickname_format[0]) {
1237 if (!nickname || !nickname[0])
1240 cp = client->internal->params->nickname_format;
1258 /* Get separator character */
1271 /* Parse the nickname */
1275 if (strchr(nickname, s))
1276 nick = strchr(nickname, s) + 1;
1278 if (strchr(nick, e))
1279 len = strchr(nick, e) - nick;
1283 *ret_nick = silc_memdup(nick, len);
1287 SILC_LOG_DEBUG(("Parsed nickname: %s", *ret_nick));
1292 /************************ Channel Searching Locally *************************/
1294 /* Finds entry for channel by the channel name. Returns the entry or NULL
1295 if the entry was not found. It is found only if the client is joined
1298 SilcChannelEntry silc_client_get_channel(SilcClient client,
1299 SilcClientConnection conn,
1302 SilcIDCacheEntry id_cache;
1303 SilcChannelEntry entry;
1305 if (!client || !conn || !channel)
1308 SILC_LOG_DEBUG(("Find channel %s", channel));
1310 /* Normalize name for search */
1311 channel = silc_channel_name_check(channel, strlen(channel), SILC_STRING_UTF8,
1316 silc_mutex_lock(conn->internal->lock);
1318 if (!silc_idcache_find_by_name_one(conn->internal->channel_cache, channel,
1320 silc_mutex_unlock(conn->internal->lock);
1325 SILC_LOG_DEBUG(("Found"));
1327 entry = id_cache->context;
1330 silc_client_ref_channel(client, conn, entry);
1331 silc_mutex_unlock(conn->internal->lock);
1338 /* Finds entry for channel by the channel ID. Returns the entry or NULL
1339 if the entry was not found. It is found only if the client is joined
1342 SilcChannelEntry silc_client_get_channel_by_id(SilcClient client,
1343 SilcClientConnection conn,
1344 SilcChannelID *channel_id)
1346 SilcIDCacheEntry id_cache;
1347 SilcChannelEntry entry;
1349 if (!client || !conn || !channel_id)
1352 SILC_LOG_DEBUG(("Find channel by id %s",
1353 silc_id_render(channel_id, SILC_ID_CHANNEL)));
1355 silc_mutex_lock(conn->internal->lock);
1357 if (!silc_idcache_find_by_id_one(conn->internal->channel_cache, channel_id,
1359 silc_mutex_unlock(conn->internal->lock);
1363 SILC_LOG_DEBUG(("Found"));
1365 entry = id_cache->context;
1368 silc_client_ref_channel(client, conn, entry);
1369 silc_mutex_unlock(conn->internal->lock);
1374 /********************** Channel Resolving from Server ***********************/
1376 /* Channel resolving context */
1379 SilcGetChannelCallback completion;
1381 } *SilcClientGetChannelInternal;
1383 /* Resolving command callback */
1385 static SilcBool silc_client_get_channel_cb(SilcClient client,
1386 SilcClientConnection conn,
1387 SilcCommand command,
1393 SilcClientGetChannelInternal i = context;
1394 SilcChannelEntry entry;
1396 if (error != SILC_STATUS_OK) {
1397 SILC_LOG_DEBUG(("Resolving failed: %s", silc_get_status_message(error)));
1399 i->completion(client, conn, error, NULL, i->context);
1403 /* Add the returned channel to list */
1404 if (i->completion) {
1405 entry = va_arg(ap, SilcChannelEntry);
1406 silc_client_ref_channel(client, conn, entry);
1407 silc_dlist_add(i->channels, entry);
1410 if (status == SILC_STATUS_OK || status == SILC_STATUS_LIST_END) {
1411 /* Deliver the channels to the caller */
1412 if (i->completion) {
1413 SILC_LOG_DEBUG(("Resolved %d channels", silc_dlist_count(i->channels)));
1414 silc_dlist_start(i->channels);
1415 i->completion(client, conn, SILC_STATUS_OK, i->channels, i->context);
1423 silc_client_list_free_channels(client, conn, i->channels);
1428 /* Resolves channel entry from the server by the channel name. */
1430 void silc_client_get_channel_resolve(SilcClient client,
1431 SilcClientConnection conn,
1433 SilcGetChannelCallback completion,
1436 SilcClientGetChannelInternal i;
1438 if (!client || !conn || !channel_name || !completion)
1441 SILC_LOG_DEBUG(("Resolve channel %s", channel_name));
1443 i = silc_calloc(1, sizeof(*i));
1446 i->completion = completion;
1447 i->context = context;
1448 i->channels = silc_dlist_init();
1454 /* Send the command */
1455 if (!silc_client_command_send(client, conn, SILC_COMMAND_IDENTIFY,
1456 silc_client_get_channel_cb, i, 1,
1457 3, channel_name, strlen(channel_name))) {
1459 completion(client, conn, SILC_STATUS_ERR_RESOURCE_LIMIT, NULL, context);
1463 /* Resolves channel information from the server by the channel ID. */
1466 silc_client_get_channel_by_id_resolve(SilcClient client,
1467 SilcClientConnection conn,
1468 SilcChannelID *channel_id,
1469 SilcGetChannelCallback completion,
1472 SilcClientGetChannelInternal i;
1474 SilcUInt16 cmd_ident;
1476 if (!client || !conn || !channel_id || !completion)
1479 SILC_LOG_DEBUG(("Resolve channel by id %s",
1480 silc_id_render(channel_id, SILC_ID_CHANNEL)));
1482 i = silc_calloc(1, sizeof(*i));
1485 i->completion = completion;
1486 i->context = context;
1487 i->channels = silc_dlist_init();
1493 /* Send the command */
1494 idp = silc_id_payload_encode(channel_id, SILC_ID_CHANNEL);
1495 cmd_ident = silc_client_command_send(client, conn, SILC_COMMAND_IDENTIFY,
1496 silc_client_get_channel_cb, i, 1,
1497 5, silc_buffer_datalen(idp));
1498 silc_buffer_free(idp);
1499 if (!cmd_ident && completion)
1500 completion(client, conn, SILC_STATUS_ERR_RESOURCE_LIMIT, NULL, context);
1505 /************************* Channel Entry Routines ***************************/
1507 /* Add new channel entry to the ID Cache */
1509 SilcChannelEntry silc_client_add_channel(SilcClient client,
1510 SilcClientConnection conn,
1511 const char *channel_name,
1513 SilcChannelID *channel_id)
1515 SilcChannelEntry channel;
1516 char *channel_namec;
1518 SILC_LOG_DEBUG(("Start"));
1520 channel = silc_calloc(1, sizeof(*channel));
1524 silc_rwlock_alloc(&channel->internal.lock);
1525 silc_atomic_init16(&channel->internal.refcnt, 0);
1526 channel->id = *channel_id;
1527 channel->mode = mode;
1529 channel->channel_name = strdup(channel_name);
1530 if (!channel->channel_name) {
1535 channel->user_list = silc_hash_table_alloc(1, silc_hash_ptr, NULL, NULL,
1536 NULL, NULL, NULL, TRUE);
1537 if (!channel->user_list) {
1538 silc_free(channel->channel_name);
1543 /* Normalize channel name */
1544 channel_namec = silc_channel_name_check(channel_name, strlen(channel_name),
1545 SILC_STRING_UTF8, 256, NULL);
1546 if (!channel_namec) {
1547 silc_free(channel->channel_name);
1548 silc_hash_table_free(channel->user_list);
1553 silc_mutex_lock(conn->internal->lock);
1555 /* Add channel to cache, the normalized channel name is saved to cache */
1556 if (!silc_idcache_add(conn->internal->channel_cache, channel_namec,
1557 &channel->id, channel)) {
1558 silc_free(channel_namec);
1559 silc_free(channel->channel_name);
1560 silc_hash_table_free(channel->user_list);
1562 silc_mutex_unlock(conn->internal->lock);
1566 silc_mutex_unlock(conn->internal->lock);
1567 silc_client_ref_channel(client, conn, channel);
1569 SILC_LOG_DEBUG(("Added %p", channel));
1574 /* Removes channel from the cache by the channel entry. */
1576 SilcBool silc_client_del_channel(SilcClient client, SilcClientConnection conn,
1577 SilcChannelEntry channel)
1579 SilcIDCacheEntry id_cache;
1587 if (silc_atomic_sub_int16(&channel->internal.refcnt, 1) > 0)
1590 SILC_LOG_DEBUG(("Deleting channel %p", channel));
1592 silc_mutex_lock(conn->internal->lock);
1593 if (silc_idcache_find_by_context(conn->internal->channel_cache, channel,
1595 silc_free(id_cache->name);
1596 ret = silc_idcache_del_by_context(conn->internal->channel_cache,
1598 silc_mutex_unlock(conn->internal->lock);
1603 silc_client_empty_channel(client, conn, channel);
1604 silc_hash_table_free(channel->user_list);
1605 silc_free(channel->channel_name);
1606 silc_free(channel->topic);
1607 if (channel->founder_key)
1608 silc_pkcs_public_key_free(channel->founder_key);
1609 if (channel->internal.send_key)
1610 silc_cipher_free(channel->internal.send_key);
1611 if (channel->internal.receive_key)
1612 silc_cipher_free(channel->internal.receive_key);
1613 if (channel->internal.hmac)
1614 silc_hmac_free(channel->internal.hmac);
1615 if (channel->internal.old_channel_keys) {
1616 silc_dlist_start(channel->internal.old_channel_keys);
1617 while ((key = silc_dlist_get(channel->internal.old_channel_keys)))
1618 silc_cipher_free(key);
1619 silc_dlist_uninit(channel->internal.old_channel_keys);
1621 if (channel->internal.old_hmacs) {
1622 silc_dlist_start(channel->internal.old_hmacs);
1623 while ((hmac = silc_dlist_get(channel->internal.old_hmacs)))
1624 silc_hmac_free(hmac);
1625 silc_dlist_uninit(channel->internal.old_hmacs);
1627 if (channel->channel_pubkeys)
1628 silc_argument_list_free(channel->channel_pubkeys,
1629 SILC_ARGUMENT_PUBLIC_KEY);
1630 silc_client_del_channel_private_keys(client, conn, channel);
1631 silc_atomic_uninit16(&channel->internal.refcnt);
1632 silc_rwlock_free(channel->internal.lock);
1633 silc_schedule_task_del_by_context(conn->client->schedule, channel);
1639 /* Replaces the channel ID of the `channel' to `new_id'. Returns FALSE
1640 if the ID could not be changed. This handles entry locking internally. */
1642 SilcBool silc_client_replace_channel_id(SilcClient client,
1643 SilcClientConnection conn,
1644 SilcChannelEntry channel,
1645 SilcChannelID *new_id)
1647 SilcBool ret = FALSE;
1652 SILC_LOG_DEBUG(("Old Channel ID id(%s)",
1653 silc_id_render(&channel->id, SILC_ID_CHANNEL)));
1654 SILC_LOG_DEBUG(("New Channel ID id(%s)",
1655 silc_id_render(new_id, SILC_ID_CHANNEL)));
1658 silc_rwlock_wrlock(channel->internal.lock);
1659 silc_mutex_lock(conn->internal->lock);
1660 silc_idcache_update_by_context(conn->internal->channel_cache, channel,
1661 new_id, NULL, FALSE);
1662 silc_mutex_unlock(conn->internal->lock);
1663 silc_rwlock_unlock(channel->internal.lock);
1670 void silc_client_lock_channel(SilcChannelEntry channel_entry)
1672 silc_rwlock_rdlock(channel_entry->internal.lock);
1675 /* Unlock channel */
1677 void silc_client_unlock_channel(SilcChannelEntry channel_entry)
1679 silc_rwlock_unlock(channel_entry->internal.lock);
1682 /* Take reference of channel entry */
1684 SilcChannelEntry silc_client_ref_channel(SilcClient client,
1685 SilcClientConnection conn,
1686 SilcChannelEntry channel_entry)
1688 silc_atomic_add_int16(&channel_entry->internal.refcnt, 1);
1689 SILC_LOG_DEBUG(("Channel %p refcnt %d->%d", channel_entry,
1690 silc_atomic_get_int16(&channel_entry->internal.refcnt) - 1,
1691 silc_atomic_get_int16(&channel_entry->internal.refcnt)));
1692 return channel_entry;
1695 /* Release reference of channel entry */
1697 void silc_client_unref_channel(SilcClient client, SilcClientConnection conn,
1698 SilcChannelEntry channel_entry)
1700 if (channel_entry) {
1701 SILC_LOG_DEBUG(("Channel %p refcnt %d->%d", channel_entry,
1702 silc_atomic_get_int16(&channel_entry->internal.refcnt),
1703 silc_atomic_get_int16(&channel_entry->internal.refcnt)
1705 silc_client_del_channel(client, conn, channel_entry);
1709 /* Free channel entry list */
1711 void silc_client_list_free_channels(SilcClient client,
1712 SilcClientConnection conn,
1713 SilcDList channel_list)
1715 SilcChannelEntry channel_entry;
1718 silc_dlist_start(channel_list);
1719 while ((channel_entry = silc_dlist_get(channel_list)))
1720 silc_client_unref_channel(client, conn, channel_entry);
1722 silc_dlist_uninit(channel_list);
1726 /************************* Server Searching Locally *************************/
1728 /* Finds entry for server by the server name. */
1730 SilcServerEntry silc_client_get_server(SilcClient client,
1731 SilcClientConnection conn,
1734 SilcIDCacheEntry id_cache;
1735 SilcServerEntry entry;
1737 if (!client || !conn || !server_name)
1740 SILC_LOG_DEBUG(("Find server by name %s", server_name));
1742 /* Normalize server name for search */
1743 server_name = silc_identifier_check(server_name, strlen(server_name),
1744 SILC_STRING_UTF8, 256, NULL);
1748 silc_mutex_lock(conn->internal->lock);
1750 if (!silc_idcache_find_by_name_one(conn->internal->server_cache,
1751 server_name, &id_cache)) {
1752 silc_free(server_name);
1753 silc_mutex_unlock(conn->internal->lock);
1757 SILC_LOG_DEBUG(("Found"));
1760 entry = id_cache->context;
1761 silc_client_ref_server(client, conn, entry);
1763 silc_mutex_unlock(conn->internal->lock);
1765 silc_free(server_name);
1770 /* Finds entry for server by the server ID. */
1772 SilcServerEntry silc_client_get_server_by_id(SilcClient client,
1773 SilcClientConnection conn,
1774 SilcServerID *server_id)
1776 SilcIDCacheEntry id_cache;
1777 SilcServerEntry entry;
1779 if (!client || !conn || !server_id)
1782 SILC_LOG_DEBUG(("Find server by id %s",
1783 silc_id_render(server_id, SILC_ID_SERVER)));
1785 silc_mutex_lock(conn->internal->lock);
1787 if (!silc_idcache_find_by_id_one(conn->internal->server_cache,
1788 server_id, &id_cache)) {
1789 silc_mutex_unlock(conn->internal->lock);
1793 SILC_LOG_DEBUG(("Found"));
1796 entry = id_cache->context;
1797 silc_client_ref_server(client, conn, entry);
1799 silc_mutex_unlock(conn->internal->lock);
1804 /*********************** Server Resolving from Server ***********************/
1806 /* Resolving context */
1809 SilcGetServerCallback completion;
1811 } *SilcClientGetServerInternal;
1813 /* Resolving command callback */
1815 static SilcBool silc_client_get_server_cb(SilcClient client,
1816 SilcClientConnection conn,
1817 SilcCommand command,
1823 SilcClientGetServerInternal i = context;
1824 SilcServerEntry server;
1826 if (error != SILC_STATUS_OK) {
1827 SILC_LOG_DEBUG(("Resolving failed: %s", silc_get_status_message(error)));
1829 i->completion(client, conn, error, NULL, i->context);
1833 /* Add the returned servers to list */
1834 if (i->completion) {
1835 server = va_arg(ap, SilcServerEntry);
1836 silc_client_ref_server(client, conn, server);
1837 silc_dlist_add(i->servers, server);
1838 server->internal.resolve_cmd_ident = 0;
1841 if (status == SILC_STATUS_OK || status == SILC_STATUS_LIST_END) {
1842 /* Deliver the servers to the caller */
1843 if (i->completion) {
1844 SILC_LOG_DEBUG(("Resolved %d servers", silc_dlist_count(i->servers)));
1845 silc_dlist_start(i->servers);
1846 i->completion(client, conn, SILC_STATUS_OK, i->servers, i->context);
1854 silc_client_list_free_servers(client, conn, i->servers);
1859 /* Resolve server by server ID */
1862 silc_client_get_server_by_id_resolve(SilcClient client,
1863 SilcClientConnection conn,
1864 SilcServerID *server_id,
1865 SilcGetServerCallback completion,
1868 SilcClientGetServerInternal i;
1869 SilcServerEntry server;
1871 SilcUInt16 cmd_ident;
1873 if (!client || !conn || !server_id || !completion)
1876 SILC_LOG_DEBUG(("Resolve server by id %s",
1877 silc_id_render(server_id, SILC_ID_SERVER)));
1879 i = silc_calloc(1, sizeof(*i));
1882 i->completion = completion;
1883 i->context = context;
1884 i->servers = silc_dlist_init();
1890 /* Attach to resolving, if on going */
1891 server = silc_client_get_server_by_id(client, conn, server_id);
1892 if (server && server->internal.resolve_cmd_ident) {
1893 SILC_LOG_DEBUG(("Attach to existing resolving"));
1894 silc_client_unref_server(client, conn, server);
1895 silc_client_command_pending(conn, SILC_COMMAND_NONE,
1896 server->internal.resolve_cmd_ident,
1897 silc_client_get_server_cb, i);
1898 return server->internal.resolve_cmd_ident;
1901 /* Send the command */
1902 idp = silc_id_payload_encode(server_id, SILC_ID_SERVER);
1903 cmd_ident = silc_client_command_send(client, conn, SILC_COMMAND_IDENTIFY,
1904 silc_client_get_server_cb, i, 1,
1905 5, silc_buffer_datalen(idp));
1906 silc_buffer_free(idp);
1907 if (!cmd_ident && completion)
1908 completion(client, conn, SILC_STATUS_ERR_RESOURCE_LIMIT, NULL, context);
1910 if (server && cmd_ident)
1911 server->internal.resolve_cmd_ident = cmd_ident;
1913 silc_client_unref_server(client, conn, server);
1918 /************************** Server Entry Routines ***************************/
1920 /* Add new server entry */
1922 SilcServerEntry silc_client_add_server(SilcClient client,
1923 SilcClientConnection conn,
1924 const char *server_name,
1925 const char *server_info,
1926 SilcServerID *server_id)
1928 SilcServerEntry server_entry;
1929 char *server_namec = NULL;
1934 SILC_LOG_DEBUG(("Adding new server %s", server_name));
1936 server_entry = silc_calloc(1, sizeof(*server_entry));
1940 silc_rwlock_alloc(&server_entry->internal.lock);
1941 silc_atomic_init8(&server_entry->internal.refcnt, 0);
1942 server_entry->id = *server_id;
1944 server_entry->server_name = strdup(server_name);
1946 server_entry->server_info = strdup(server_info);
1948 /* Normalize server name */
1950 server_namec = silc_identifier_check(server_name, strlen(server_name),
1951 SILC_STRING_UTF8, 256, NULL);
1952 if (!server_namec) {
1953 silc_free(server_entry->server_name);
1954 silc_free(server_entry->server_info);
1955 silc_free(server_entry);
1960 silc_mutex_lock(conn->internal->lock);
1962 /* Add server to cache */
1963 if (!silc_idcache_add(conn->internal->server_cache, server_namec,
1964 &server_entry->id, server_entry)) {
1965 silc_free(server_namec);
1966 silc_free(server_entry->server_name);
1967 silc_free(server_entry->server_info);
1968 silc_free(server_entry);
1969 silc_mutex_unlock(conn->internal->lock);
1973 silc_mutex_unlock(conn->internal->lock);
1974 silc_client_ref_server(client, conn, server_entry);
1976 SILC_LOG_DEBUG(("Added %p", server_entry));
1978 return server_entry;
1981 /* Removes server from the cache by the server entry. */
1983 SilcBool silc_client_del_server(SilcClient client, SilcClientConnection conn,
1984 SilcServerEntry server)
1986 SilcIDCacheEntry id_cache;
1992 if (silc_atomic_sub_int8(&server->internal.refcnt, 1) > 0)
1995 SILC_LOG_DEBUG(("Deleting server %p", server));
1997 silc_mutex_lock(conn->internal->lock);
1998 if (silc_idcache_find_by_context(conn->internal->server_cache, server,
2000 silc_free(id_cache->name);
2001 ret = silc_idcache_del_by_context(conn->internal->server_cache,
2003 silc_mutex_unlock(conn->internal->lock);
2005 silc_free(server->server_name);
2006 silc_free(server->server_info);
2007 if (server->public_key)
2008 silc_pkcs_public_key_free(server->public_key);
2009 silc_atomic_uninit8(&server->internal.refcnt);
2010 silc_rwlock_free(server->internal.lock);
2016 /* Updates the `server_entry' with the new information sent as argument. */
2018 void silc_client_update_server(SilcClient client,
2019 SilcClientConnection conn,
2020 SilcServerEntry server_entry,
2021 const char *server_name,
2022 const char *server_info)
2024 char *server_namec = NULL;
2026 SILC_LOG_DEBUG(("Updating server %p", server_entry));
2029 (!server_entry->server_name ||
2030 !silc_utf8_strcasecmp(server_entry->server_name, server_name))) {
2032 server_namec = silc_identifier_check(server_name, strlen(server_name),
2033 SILC_STRING_UTF8, 256, NULL);
2037 silc_free(server_entry->server_name);
2038 server_entry->server_name = strdup(server_name);
2039 if (!server_entry->server_name)
2042 /* Update cache entry */
2043 silc_mutex_lock(conn->internal->lock);
2044 silc_idcache_update_by_context(conn->internal->server_cache, server_entry,
2045 NULL, server_namec, TRUE);
2046 silc_mutex_unlock(conn->internal->lock);
2050 (!server_entry->server_info ||
2051 !silc_utf8_strcasecmp(server_entry->server_info, server_info))) {
2052 silc_free(server_entry->server_info);
2053 server_entry->server_info = strdup(server_info);
2059 void silc_client_lock_server(SilcServerEntry server_entry)
2061 silc_rwlock_rdlock(server_entry->internal.lock);
2066 void silc_client_unlock_server(SilcServerEntry server_entry)
2068 silc_rwlock_unlock(server_entry->internal.lock);
2071 /* Take reference of server entry */
2073 SilcServerEntry silc_client_ref_server(SilcClient client,
2074 SilcClientConnection conn,
2075 SilcServerEntry server_entry)
2077 silc_atomic_add_int8(&server_entry->internal.refcnt, 1);
2078 SILC_LOG_DEBUG(("Server %p refcnt %d->%d", server_entry,
2079 silc_atomic_get_int8(&server_entry->internal.refcnt) - 1,
2080 silc_atomic_get_int8(&server_entry->internal.refcnt)));
2081 return server_entry;
2084 /* Release reference of server entry */
2086 void silc_client_unref_server(SilcClient client, SilcClientConnection conn,
2087 SilcServerEntry server_entry)
2090 SILC_LOG_DEBUG(("Server %p refcnt %d->%d", server_entry,
2091 silc_atomic_get_int8(&server_entry->internal.refcnt),
2092 silc_atomic_get_int8(&server_entry->internal.refcnt)
2094 silc_client_del_server(client, conn, server_entry);
2098 /* Free server entry list */
2100 void silc_client_list_free_servers(SilcClient client,
2101 SilcClientConnection conn,
2102 SilcDList server_list)
2104 SilcServerEntry server_entry;
2107 silc_dlist_start(server_list);
2108 while ((server_entry = silc_dlist_get(server_list)))
2109 silc_client_unref_server(client, conn, server_entry);
2111 silc_dlist_uninit(server_list);