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) {
261 SILC_LOG_ERROR(("Missing arguments to "
262 "silc_client_get_clients_by_id_resolve call"));
266 SILC_LOG_DEBUG(("Resolve client by ID (%s)",
267 silc_id_render(client_id, SILC_ID_CLIENT)));
269 i = silc_calloc(1, sizeof(*i));
272 i->completion = completion;
273 i->context = context;
274 i->clients = silc_dlist_init();
280 /* Attach to resolving, if on going */
281 client_entry = silc_client_get_client_by_id(client, conn, client_id);
282 if (client_entry && client_entry->internal.resolve_cmd_ident) {
283 SILC_LOG_DEBUG(("Attach to existing resolving"));
284 silc_client_unref_client(client, conn, client_entry);
285 silc_client_command_pending(conn, SILC_COMMAND_NONE,
286 client_entry->internal.resolve_cmd_ident,
287 silc_client_get_clients_cb, i);
288 return client_entry->internal.resolve_cmd_ident;
291 /* Send the command */
292 idp = silc_id_payload_encode(client_id, SILC_ID_CLIENT);
293 cmd_ident = silc_client_command_send(client, conn, SILC_COMMAND_WHOIS,
294 silc_client_get_clients_cb, i,
295 2, 3, silc_buffer_datalen(attributes),
296 4, silc_buffer_datalen(idp));
297 if (!cmd_ident && completion)
298 completion(client, conn, SILC_STATUS_ERR_RESOURCE_LIMIT, NULL, context);
300 if (client_entry && cmd_ident) {
301 client_entry->internal.resolve_cmd_ident = cmd_ident;
302 i->client_entry = client_entry;
304 silc_client_unref_client(client, conn, client_entry);
307 silc_buffer_free(idp);
312 /* Finds client entry or entries by the `nickname' and `server'. The
313 completion callback will be called when the client entries has been
314 found. Used internally by the library. */
316 static SilcUInt16 silc_client_get_clients_i(SilcClient client,
317 SilcClientConnection conn,
319 const char *nickname,
321 SilcBuffer attributes,
322 SilcGetClientCallback completion,
325 SilcClientGetClientInternal i;
326 char nick[128 + 1], serv[256 + 1], userhost[768 + 1], *parsed = NULL;
329 SILC_LOG_DEBUG(("Resolve client by %s command",
330 silc_get_command_name(command)));
332 if (!client || !conn) {
333 SILC_LOG_ERROR(("Missing arguments to silc_client_get_clients call"));
336 if (!nickname && !attributes) {
337 SILC_LOG_ERROR(("Missing arguments to silc_client_get_clients call"));
341 /* Parse server name from the nickname if set */
342 if (silc_parse_userfqdn(nickname, nick, sizeof(nick),
343 serv, sizeof(serv) == 2))
344 server = (const char *)serv;
345 nickname = (const char *)nick;
347 /* Parse nickname in case it is formatted */
348 if (silc_client_nickname_parse(client, conn, (char *)nickname, &parsed))
349 nickname = (const char *)parsed;
351 i = silc_calloc(1, sizeof(*i));
356 i->clients = silc_dlist_init();
362 i->completion = completion;
363 i->context = context;
365 memset(userhost, 0, sizeof(userhost));
366 if (nickname && server) {
367 len = strlen(nickname) + strlen(server) + 3;
368 silc_strncat(userhost, len, nickname, strlen(nickname));
369 silc_strncat(userhost, len, "@", 1);
370 silc_strncat(userhost, len, server, strlen(server));
371 } else if (nickname) {
372 silc_strncat(userhost, sizeof(userhost) - 1, nickname, strlen(nickname));
376 /* Send the command */
377 if (command == SILC_COMMAND_IDENTIFY)
378 return silc_client_command_send(client, conn, SILC_COMMAND_IDENTIFY,
379 silc_client_get_clients_cb, i,
380 1, 1, userhost, strlen(userhost));
381 return silc_client_command_send(client, conn, SILC_COMMAND_WHOIS,
382 silc_client_get_clients_cb, i,
383 2, 1, userhost, strlen(userhost),
384 3, silc_buffer_datalen(attributes));
387 /* Get clients from server with IDENTIFY command */
389 SilcUInt16 silc_client_get_clients(SilcClient client,
390 SilcClientConnection conn,
391 const char *nickname,
393 SilcGetClientCallback completion,
396 return silc_client_get_clients_i(client, conn, SILC_COMMAND_IDENTIFY,
397 nickname, server, NULL,
398 completion, context);
401 /* Get clients from server with WHOIS command */
403 SilcUInt16 silc_client_get_clients_whois(SilcClient client,
404 SilcClientConnection conn,
405 const char *nickname,
407 SilcBuffer attributes,
408 SilcGetClientCallback completion,
411 return silc_client_get_clients_i(client, conn, SILC_COMMAND_WHOIS,
412 nickname, server, attributes,
413 completion, context);
416 /* ID list resolving context */
418 SilcGetClientCallback completion;
420 SilcBuffer client_id_list;
421 SilcUInt32 list_count;
422 } *GetClientsByListInternal;
424 static SilcBool silc_client_get_clients_list_cb(SilcClient client,
425 SilcClientConnection conn,
432 GetClientsByListInternal i = context;
433 SilcClientEntry client_entry;
439 /* Process the list after all replies have been received */
440 if (status != SILC_STATUS_OK && !SILC_STATUS_IS_ERROR(status) &&
441 status != SILC_STATUS_LIST_END)
444 SILC_LOG_DEBUG(("Resolved all clients"));
446 clients = silc_dlist_init();
448 status = SILC_STATUS_ERR_RESOURCE_LIMIT;
452 for (c = 0; c < i->list_count; c++) {
454 SILC_GET16_MSB(idp_len, i->client_id_list->data + 2);
456 if (!silc_id_payload_parse_id(i->client_id_list->data, idp_len, &id)) {
457 status = SILC_STATUS_ERR_BAD_CLIENT_ID;
461 /* Get client entry */
462 client_entry = silc_client_get_client_by_id(client, conn, &id.u.client_id);
464 silc_dlist_add(clients, client_entry);
466 if (!silc_buffer_pull(i->client_id_list, idp_len)) {
467 status = SILC_STATUS_ERR_BAD_CLIENT_ID;
472 silc_dlist_start(clients);
473 status = SILC_STATUS_OK;
475 i->completion(client, conn, status, clients, i->context);
478 if (status != SILC_STATUS_OK && i->completion)
479 i->completion(client, conn, status, NULL, i->context);
481 silc_client_list_free(client, conn, clients);
482 silc_buffer_free(i->client_id_list);
488 /* Gets client entries by the list of client ID's `client_id_list'. This
489 always resolves those client ID's it does not know yet from the server
490 so this function might take a while. The `client_id_list' is a list
491 of ID Payloads added one after other. JOIN command reply and USERS
492 command reply for example returns this sort of list. The `completion'
493 will be called after the entries are available. */
495 SilcUInt16 silc_client_get_clients_by_list(SilcClient client,
496 SilcClientConnection conn,
497 SilcUInt32 list_count,
498 SilcBuffer client_id_list,
499 SilcGetClientCallback completion,
502 GetClientsByListInternal in;
503 SilcClientEntry entry;
504 unsigned char **res_argv = NULL;
505 SilcUInt32 *res_argv_lens = NULL, *res_argv_types = NULL, res_argc = 0;
506 SilcUInt16 idp_len, cmd_ident;
511 SILC_LOG_DEBUG(("Resolve clients from Client ID list"));
513 if (!client || !conn || !client_id_list)
516 in = silc_calloc(1, sizeof(*in));
519 in->completion = completion;
520 in->context = context;
521 in->list_count = list_count;
522 in->client_id_list = silc_buffer_copy(client_id_list);
523 if (!in->client_id_list)
526 for (i = 0; i < list_count; i++) {
528 SILC_GET16_MSB(idp_len, client_id_list->data + 2);
530 if (!silc_id_payload_parse_id(client_id_list->data, idp_len, &id))
533 /* Check if we have this client cached already. If we don't have the
534 entry or it has incomplete info, then resolve it from the server. */
535 entry = silc_client_get_client_by_id(client, conn, &id.u.client_id);
536 if (!entry || !entry->nickname[0] || !entry->username[0] ||
539 res_argv = silc_calloc(list_count, sizeof(*res_argv));
540 res_argv_lens = silc_calloc(list_count, sizeof(*res_argv_lens));
541 res_argv_types = silc_calloc(list_count, sizeof(*res_argv_types));
542 if (!res_argv || !res_argv_lens || !res_argv_types) {
543 silc_client_unref_client(client, conn, entry);
548 res_argv[res_argc] = client_id_list->data;
549 res_argv_lens[res_argc] = idp_len;
550 res_argv_types[res_argc] = res_argc + 4;
553 silc_client_unref_client(client, conn, entry);
555 if (!silc_buffer_pull(client_id_list, idp_len))
558 silc_buffer_start(client_id_list);
560 /* Query the unknown client information from server */
562 cmd_ident = silc_client_command_send_argv(client,
563 conn, SILC_COMMAND_WHOIS,
564 silc_client_get_clients_list_cb,
565 in, res_argc, res_argv,
569 silc_free(res_argv_lens);
570 silc_free(res_argv_types);
574 /* We have the clients in cache, get them and call the completion */
575 silc_client_get_clients_list_cb(client, conn, SILC_COMMAND_WHOIS,
576 SILC_STATUS_OK, SILC_STATUS_OK, in, tmp);
580 silc_buffer_free(in->client_id_list);
583 silc_free(res_argv_lens);
584 silc_free(res_argv_types);
591 SilcClientConnection conn;
592 SilcChannelID channel_id;
593 SilcGetClientCallback completion;
596 } *GetClientsByChannelInternal;
598 SILC_CLIENT_CMD_FUNC(get_clients_by_channel_cb)
600 GetClientsByChannelInternal i = context;
601 SilcClientEntry *clients = NULL;
602 SilcUInt32 clients_count = 0;
603 SilcBool found = FALSE;
604 SilcChannelEntry channel;
605 SilcHashTableList htl;
614 channel = silc_client_get_channel_by_id(i->client, i->conn, &i->channel_id);
615 if (channel && !silc_hash_table_count(channel->user_list)) {
616 clients = silc_calloc(silc_hash_table_count(channel->user_list),
618 silc_hash_table_list(channel->user_list, &htl);
619 while (silc_hash_table_get(&htl, NULL, (void *)&chu))
620 clients[clients_count++] = chu->client;
621 silc_hash_table_list_reset(&htl);
626 i->completion(i->client, i->conn, clients, clients_count, i->context);
629 i->completion(i->client, i->conn, NULL, 0, i->context);
635 /* Gets client entries by the channel entry indicated by `channel'. Thus,
636 it resolves the clients currently on that channel. */
638 void silc_client_get_clients_by_channel(SilcClient client,
639 SilcClientConnection conn,
640 SilcChannelEntry channel,
641 SilcGetClientCallback completion,
644 GetClientsByChannelInternal in;
645 SilcHashTableList htl;
647 SilcClientEntry entry;
648 unsigned char **res_argv = NULL;
649 SilcUInt32 *res_argv_lens = NULL, *res_argv_types = NULL, res_argc = 0;
651 SilcBool wait_res = FALSE;
653 assert(client && conn && channel);
655 SILC_LOG_DEBUG(("Start"));
657 in = silc_calloc(1, sizeof(*in));
660 in->channel_id = *channel->id;
661 in->completion = completion;
662 in->context = context;
664 /* If user list does not exist, send USERS command. */
665 if (!channel->user_list || !silc_hash_table_count(channel->user_list)) {
666 SILC_LOG_DEBUG(("Sending USERS"));
667 silc_client_command_register(client, SILC_COMMAND_USERS, NULL, NULL,
668 silc_client_command_reply_users_i, 0,
670 silc_client_command_send(client, conn, SILC_COMMAND_USERS,
671 conn->cmd_ident, 1, 2, channel->channel_name,
672 strlen(channel->channel_name));
673 silc_client_command_pending(conn, SILC_COMMAND_USERS, conn->cmd_ident,
674 silc_client_command_get_clients_by_channel_cb,
679 silc_hash_table_list(channel->user_list, &htl);
680 while (silc_hash_table_get(&htl, NULL, (void *)&chu)) {
683 /* If the entry has incomplete info, then resolve it from the server. */
684 if (!entry->nickname[0] || !entry->realname) {
685 if (entry->status & SILC_CLIENT_STATUS_RESOLVING) {
686 /* Attach to this resolving and wait until it finishes */
687 silc_client_command_pending(
688 conn, SILC_COMMAND_NONE,
689 entry->resolve_cmd_ident,
690 silc_client_command_get_clients_by_channel_cb,
696 entry->status |= SILC_CLIENT_STATUS_RESOLVING;
697 entry->resolve_cmd_ident = conn->cmd_ident + 1;
699 idp = silc_id_payload_encode(entry->id, SILC_ID_CLIENT);
701 /* No we don't have it, query it from the server. Assemble argument
702 table that will be sent for the WHOIS command later. */
703 res_argv = silc_realloc(res_argv, sizeof(*res_argv) *
705 res_argv_lens = silc_realloc(res_argv_lens, sizeof(*res_argv_lens) *
707 res_argv_types = silc_realloc(res_argv_types, sizeof(*res_argv_types) *
709 res_argv[res_argc] = silc_memdup(idp->data, idp->len);
710 res_argv_lens[res_argc] = idp->len;
711 res_argv_types[res_argc] = res_argc + 4;
714 silc_buffer_free(idp);
717 silc_hash_table_list_reset(&htl);
719 /* Query the client information from server if the list included clients
720 that we don't know about. */
724 /* Send the WHOIS command to server */
725 res_cmd = silc_command_payload_encode(SILC_COMMAND_WHOIS,
726 res_argc, res_argv, res_argv_lens,
727 res_argv_types, ++conn->cmd_ident);
728 silc_client_packet_send(client, conn->sock, SILC_PACKET_COMMAND,
729 NULL, 0, NULL, NULL, res_cmd->data, res_cmd->len,
732 /* Register our own command reply for this command */
733 silc_client_command_register(client, SILC_COMMAND_WHOIS, NULL, NULL,
734 silc_client_command_reply_whois_i, 0,
737 /* Process the applications request after reply has been received */
738 silc_client_command_pending(
739 conn, SILC_COMMAND_WHOIS, conn->cmd_ident,
740 silc_client_command_get_clients_by_channel_cb,
744 silc_buffer_free(res_cmd);
746 silc_free(res_argv_lens);
747 silc_free(res_argv_types);
754 /* We have the clients in cache, get them and call the completion */
755 silc_client_command_get_clients_by_channel_cb((void *)in, NULL);
760 /************************** Client Entry Routines ***************************/
762 /* Creates new client entry and adds it to the ID cache. Returns pointer
765 SilcClientEntry silc_client_add_client(SilcClient client,
766 SilcClientConnection conn,
767 char *nickname, char *username,
768 char *userinfo, SilcClientID *id,
771 SilcClientEntry client_entry;
774 SILC_LOG_DEBUG(("Adding new client entry"));
776 /* Save the client infos */
777 client_entry = silc_calloc(1, sizeof(*client_entry));
781 silc_rwlock_alloc(&client_entry->internal.lock);
782 silc_atomic_init8(&client_entry->internal.refcnt, 0);
783 client_entry->id = *id;
784 client_entry->mode = mode;
785 client_entry->realname = userinfo ? strdup(userinfo) : NULL;
786 silc_parse_userfqdn(nickname, client_entry->nickname,
787 sizeof(client_entry->nickname),
788 client_entry->server,
789 sizeof(client_entry->server));
790 silc_parse_userfqdn(username, client_entry->username,
791 sizeof(client_entry->username),
792 client_entry->hostname,
793 sizeof(client_entry->hostname));
794 client_entry->channels = silc_hash_table_alloc(1, silc_hash_ptr, NULL, NULL,
795 NULL, NULL, NULL, TRUE);
796 if (!client_entry->channels) {
797 silc_free(client_entry->realname);
798 silc_free(client_entry);
802 /* Normalize nickname */
803 if (client_entry->nickname[0]) {
804 nick = silc_identifier_check(client_entry->nickname,
805 strlen(client_entry->nickname),
806 SILC_STRING_UTF8, 128, NULL);
808 silc_free(client_entry->realname);
809 silc_hash_table_free(client_entry->channels);
810 silc_free(client_entry);
815 silc_mutex_lock(conn->internal->lock);
817 /* Add client to cache, the normalized nickname is saved to cache */
818 if (!silc_idcache_add(conn->internal->client_cache, nick,
819 &client_entry->id, client_entry)) {
821 silc_free(client_entry->realname);
822 silc_hash_table_free(client_entry->channels);
823 silc_free(client_entry);
824 silc_mutex_unlock(conn->internal->lock);
828 client_entry->nickname_normalized = nick;
830 silc_mutex_unlock(conn->internal->lock);
831 silc_client_ref_client(client, conn, client_entry);
833 /* Format the nickname */
834 silc_client_nickname_format(client, conn, client_entry, FALSE);
836 if (client_entry->nickname[0])
837 client_entry->internal.valid = TRUE;
839 SILC_LOG_DEBUG(("Added %p", client_entry));
844 /* Updates the `client_entry' with the new information sent as argument.
845 This handles entry locking internally. */
847 void silc_client_update_client(SilcClient client,
848 SilcClientConnection conn,
849 SilcClientEntry client_entry,
850 const char *nickname,
851 const char *username,
852 const char *userinfo,
857 SILC_LOG_DEBUG(("Update client entry"));
859 silc_rwlock_wrlock(client_entry->internal.lock);
861 if (!client_entry->realname && userinfo)
862 client_entry->realname = strdup(userinfo);
863 if ((!client_entry->username[0] || !client_entry->hostname[0]) && username)
864 silc_parse_userfqdn(username, client_entry->username,
865 sizeof(client_entry->username),
866 client_entry->hostname,
867 sizeof(client_entry->username));
868 if (!client_entry->nickname[0] && nickname) {
869 silc_parse_userfqdn(nickname, client_entry->nickname,
870 sizeof(client_entry->nickname),
871 client_entry->server,
872 sizeof(client_entry->server));
874 /* Normalize nickname */
875 nick = silc_identifier_check(client_entry->nickname,
876 strlen(client_entry->nickname),
877 SILC_STRING_UTF8, 128, NULL);
879 silc_rwlock_unlock(client_entry->internal.lock);
883 /* Format nickname */
884 silc_client_nickname_format(client, conn, client_entry,
885 client_entry == conn->local_entry);
887 /* Update cache entry */
888 silc_mutex_lock(conn->internal->lock);
889 silc_idcache_update_by_context(conn->internal->client_cache,
890 client_entry, NULL, nick, TRUE);
891 silc_mutex_unlock(conn->internal->lock);
892 client_entry->nickname_normalized = nick;
893 client_entry->internal.valid = TRUE;
895 client_entry->mode = mode;
897 silc_rwlock_unlock(client_entry->internal.lock);
900 /* Change a client's nickname. Must be called with `client_entry' locked. */
902 SilcBool silc_client_change_nickname(SilcClient client,
903 SilcClientConnection conn,
904 SilcClientEntry client_entry,
905 const char *new_nick,
906 SilcClientID *new_id,
907 const unsigned char *idp,
912 SILC_LOG_DEBUG(("Change nickname %s to %s", client_entry->nickname,
915 /* Normalize nickname */
916 tmp = silc_identifier_check(new_nick, strlen(new_nick),
917 SILC_STRING_UTF8, 128, NULL);
921 /* Update the client entry */
922 silc_mutex_lock(conn->internal->lock);
923 if (!silc_idcache_update_by_context(conn->internal->client_cache,
924 client_entry, new_id, tmp, TRUE)) {
926 silc_mutex_unlock(conn->internal->lock);
929 silc_mutex_unlock(conn->internal->lock);
931 memset(client_entry->nickname, 0, sizeof(client_entry->nickname));
932 memcpy(client_entry->nickname, new_nick, strlen(new_nick));
933 client_entry->nickname_normalized = tmp;
934 silc_client_nickname_format(client, conn, client_entry,
935 client_entry == conn->local_entry);
937 /* For my client entry, update ID and set new ID to packet stream */
938 if (client_entry == conn->local_entry) {
939 if (idp && idp_len) {
940 silc_buffer_enlarge(conn->internal->local_idp, idp_len);
941 silc_buffer_put(conn->internal->local_idp, idp, idp_len);
944 silc_packet_set_ids(conn->stream, SILC_ID_CLIENT, conn->local_id,
948 client_entry->internal.valid = TRUE;
952 /* Deletes the client entry and frees all memory. */
954 void silc_client_del_client_entry(SilcClient client,
955 SilcClientConnection conn,
956 SilcClientEntry client_entry)
958 silc_free(client_entry->realname);
959 silc_free(client_entry->nickname_normalized);
960 silc_free(client_entry->internal.key);
961 if (client_entry->public_key)
962 silc_pkcs_public_key_free(client_entry->public_key);
963 silc_hash_table_free(client_entry->channels);
964 if (client_entry->internal.send_key)
965 silc_cipher_free(client_entry->internal.send_key);
966 if (client_entry->internal.receive_key)
967 silc_cipher_free(client_entry->internal.receive_key);
968 if (client_entry->internal.hmac_send)
969 silc_hmac_free(client_entry->internal.hmac_send);
970 if (client_entry->internal.hmac_receive)
971 silc_hmac_free(client_entry->internal.hmac_receive);
972 silc_client_ftp_session_free_client(client, client_entry);
973 if (client_entry->internal.ke)
974 silc_client_abort_key_agreement(client, conn, client_entry);
975 silc_atomic_uninit8(&client_entry->internal.refcnt);
976 silc_rwlock_free(client_entry->internal.lock);
977 silc_free(client_entry);
980 /* Removes client from the cache by the client entry. */
982 SilcBool silc_client_del_client(SilcClient client, SilcClientConnection conn,
983 SilcClientEntry client_entry)
990 if (silc_atomic_sub_int8(&client_entry->internal.refcnt, 1) > 0)
993 SILC_LOG_DEBUG(("Deleting client %p", client_entry));
995 silc_mutex_lock(conn->internal->lock);
996 ret = silc_idcache_del_by_context(conn->internal->client_cache,
998 silc_mutex_unlock(conn->internal->lock);
1001 /* Remove from channels */
1002 silc_client_remove_from_channels(client, conn, client_entry);
1004 /* Free the client entry data */
1005 silc_client_del_client_entry(client, conn, client_entry);
1011 /* Internal routine used to find client by ID and if not found this creates
1012 new client entry and returns it. */
1014 SilcClientEntry silc_client_get_client(SilcClient client,
1015 SilcClientConnection conn,
1016 SilcClientID *client_id)
1018 SilcClientEntry client_entry;
1020 client_entry = silc_client_get_client_by_id(client, conn, client_id);
1021 if (!client_entry) {
1022 client_entry = silc_client_add_client(client, conn, NULL, NULL, NULL,
1026 silc_client_ref_client(client, conn, client_entry);
1029 return client_entry;
1034 void silc_client_lock_client(SilcClientEntry client_entry)
1036 silc_rwlock_rdlock(client_entry->internal.lock);
1041 void silc_client_unlock_client(SilcClientEntry client_entry)
1043 silc_rwlock_unlock(client_entry->internal.lock);
1046 /* Take reference of client entry */
1048 SilcClientEntry silc_client_ref_client(SilcClient client,
1049 SilcClientConnection conn,
1050 SilcClientEntry client_entry)
1052 silc_atomic_add_int8(&client_entry->internal.refcnt, 1);
1053 SILC_LOG_DEBUG(("Client %p refcnt %d->%d", client_entry,
1054 silc_atomic_get_int8(&client_entry->internal.refcnt) - 1,
1055 silc_atomic_get_int8(&client_entry->internal.refcnt)));
1056 return client_entry;
1059 /* Release reference of client entry */
1061 void silc_client_unref_client(SilcClient client, SilcClientConnection conn,
1062 SilcClientEntry client_entry)
1065 SILC_LOG_DEBUG(("Client %p refcnt %d->%d", client_entry,
1066 silc_atomic_get_int8(&client_entry->internal.refcnt),
1067 silc_atomic_get_int8(&client_entry->internal.refcnt) - 1));
1068 silc_client_del_client(client, conn, client_entry);
1072 /* Free client entry list */
1074 void silc_client_list_free(SilcClient client, SilcClientConnection conn,
1075 SilcDList client_list)
1077 SilcClientEntry client_entry;
1080 silc_dlist_start(client_list);
1081 while ((client_entry = silc_dlist_get(client_list)))
1082 silc_client_unref_client(client, conn, client_entry);
1084 silc_dlist_uninit(client_list);
1088 /* Formats the nickname of the client specified by the `client_entry'.
1089 If the format is specified by the application this will format the
1090 nickname and replace the old nickname in the client entry. If the
1091 format string is not specified then this function has no effect.
1092 Returns the client entry that was formatted. */
1094 SilcClientEntry silc_client_nickname_format(SilcClient client,
1095 SilcClientConnection conn,
1096 SilcClientEntry client_entry,
1100 char newnick[128 + 1];
1101 int i, off = 0, len;
1104 SilcClientEntry entry, unformatted = NULL;
1106 if (!client->internal->params->nickname_format[0])
1107 return client_entry;
1108 if (!client_entry->nickname[0])
1111 SILC_LOG_DEBUG(("Format nickname"));
1113 /* Get all clients with same nickname. Do not perform the formatting
1114 if there aren't any clients with same nickname unless the application
1115 is forcing us to do so. */
1116 clients = silc_client_get_clients_local_ext(client, conn,
1117 client_entry->nickname,
1121 if (silc_dlist_count(clients) == 1 &&
1122 !client->internal->params->nickname_force_format) {
1123 silc_client_list_free(client, conn, clients);
1124 return client_entry;
1129 while ((entry = silc_dlist_get(clients))) {
1130 if (entry->internal.valid && entry != client_entry)
1132 if (entry->internal.valid && entry != client_entry &&
1133 silc_utf8_strcasecmp(entry->nickname, client_entry->nickname)) {
1135 unformatted = entry;
1139 if (!len || freebase) {
1140 silc_client_list_free(client, conn, clients);
1141 return client_entry;
1144 /* If priority formatting, this client always gets unformatted nickname. */
1145 if (unformatted && priority)
1146 client_entry = unformatted;
1148 memset(newnick, 0, sizeof(newnick));
1149 cp = client->internal->params->nickname_format;
1159 if (!client_entry->nickname[0])
1161 len = strlen(client_entry->nickname);
1162 memcpy(&newnick[off], client_entry->nickname, len);
1166 /* Stripped hostname */
1167 if (!client_entry->hostname[0])
1169 len = strcspn(client_entry->hostname, ".");
1170 i = strcspn(client_entry->hostname, "-");
1173 memcpy(&newnick[off], client_entry->hostname, len);
1178 if (!client_entry->hostname[0])
1180 len = strlen(client_entry->hostname);
1181 memcpy(&newnick[off], client_entry->hostname, len);
1185 /* Ascending number */
1190 if (silc_dlist_count(clients) == 1)
1193 silc_dlist_start(clients);
1194 while ((entry = silc_dlist_get(clients))) {
1195 if (!silc_utf8_strncasecmp(entry->nickname, newnick, off))
1197 if (strlen(entry->nickname) <= off)
1199 num = atoi(&entry->nickname[off]);
1204 memset(tmp, 0, sizeof(tmp));
1205 silc_snprintf(tmp, sizeof(tmp) - 1, "%d", ++max);
1207 memcpy(&newnick[off], tmp, len);
1212 /* Some other character in the string */
1213 memcpy(&newnick[off], cp, 1);
1222 memcpy(client_entry->nickname, newnick, strlen(newnick));
1223 silc_client_list_free(client, conn, clients);
1225 return client_entry;
1228 /* Parses nickname according to nickname format string */
1230 SilcBool silc_client_nickname_parse(SilcClient client,
1231 SilcClientConnection conn,
1235 char *cp, s = 0, e = 0, *nick;
1239 if (!client->internal->params->nickname_format[0]) {
1244 if (!nickname || !nickname[0])
1247 cp = client->internal->params->nickname_format;
1265 /* Get separator character */
1278 /* Parse the nickname */
1282 if (strchr(nickname, s))
1283 nick = strchr(nickname, s) + 1;
1285 if (strchr(nick, e))
1286 len = strchr(nick, e) - nick;
1290 *ret_nick = silc_memdup(nick, len);
1294 SILC_LOG_DEBUG(("Parsed nickname: %s", *ret_nick));
1299 /************************ Channel Searching Locally *************************/
1301 /* Finds entry for channel by the channel name. Returns the entry or NULL
1302 if the entry was not found. It is found only if the client is joined
1305 SilcChannelEntry silc_client_get_channel(SilcClient client,
1306 SilcClientConnection conn,
1309 SilcIDCacheEntry id_cache;
1310 SilcChannelEntry entry;
1312 if (!client || !conn || !channel)
1315 SILC_LOG_DEBUG(("Find channel %s", channel));
1317 /* Normalize name for search */
1318 channel = silc_channel_name_check(channel, strlen(channel), SILC_STRING_UTF8,
1323 silc_mutex_lock(conn->internal->lock);
1325 if (!silc_idcache_find_by_name_one(conn->internal->channel_cache, channel,
1327 silc_mutex_unlock(conn->internal->lock);
1332 SILC_LOG_DEBUG(("Found"));
1334 entry = id_cache->context;
1337 silc_client_ref_channel(client, conn, entry);
1338 silc_mutex_unlock(conn->internal->lock);
1345 /* Finds entry for channel by the channel ID. Returns the entry or NULL
1346 if the entry was not found. It is found only if the client is joined
1349 SilcChannelEntry silc_client_get_channel_by_id(SilcClient client,
1350 SilcClientConnection conn,
1351 SilcChannelID *channel_id)
1353 SilcIDCacheEntry id_cache;
1354 SilcChannelEntry entry;
1356 if (!client || !conn || !channel_id)
1359 SILC_LOG_DEBUG(("Find channel by id %s",
1360 silc_id_render(channel_id, SILC_ID_CHANNEL)));
1362 silc_mutex_lock(conn->internal->lock);
1364 if (!silc_idcache_find_by_id_one(conn->internal->channel_cache, channel_id,
1366 silc_mutex_unlock(conn->internal->lock);
1370 SILC_LOG_DEBUG(("Found"));
1372 entry = id_cache->context;
1375 silc_client_ref_channel(client, conn, entry);
1376 silc_mutex_unlock(conn->internal->lock);
1381 /********************** Channel Resolving from Server ***********************/
1383 /* Channel resolving context */
1386 SilcGetChannelCallback completion;
1388 } *SilcClientGetChannelInternal;
1390 /* Resolving command callback */
1392 static SilcBool silc_client_get_channel_cb(SilcClient client,
1393 SilcClientConnection conn,
1394 SilcCommand command,
1400 SilcClientGetChannelInternal i = context;
1401 SilcChannelEntry entry;
1403 if (error != SILC_STATUS_OK) {
1404 SILC_LOG_DEBUG(("Resolving failed: %s", silc_get_status_message(error)));
1406 i->completion(client, conn, error, NULL, i->context);
1410 /* Add the returned channel to list */
1411 if (i->completion) {
1412 entry = va_arg(ap, SilcChannelEntry);
1413 silc_client_ref_channel(client, conn, entry);
1414 silc_dlist_add(i->channels, entry);
1417 if (status == SILC_STATUS_OK || status == SILC_STATUS_LIST_END) {
1418 /* Deliver the channels to the caller */
1419 if (i->completion) {
1420 SILC_LOG_DEBUG(("Resolved %d channels", silc_dlist_count(i->channels)));
1421 silc_dlist_start(i->channels);
1422 i->completion(client, conn, SILC_STATUS_OK, i->channels, i->context);
1430 silc_client_list_free_channels(client, conn, i->channels);
1435 /* Resolves channel entry from the server by the channel name. */
1437 void silc_client_get_channel_resolve(SilcClient client,
1438 SilcClientConnection conn,
1440 SilcGetChannelCallback completion,
1443 SilcClientGetChannelInternal i;
1445 if (!client || !conn || !channel_name || !completion)
1448 SILC_LOG_DEBUG(("Resolve channel %s", channel_name));
1450 i = silc_calloc(1, sizeof(*i));
1453 i->completion = completion;
1454 i->context = context;
1455 i->channels = silc_dlist_init();
1461 /* Send the command */
1462 if (!silc_client_command_send(client, conn, SILC_COMMAND_IDENTIFY,
1463 silc_client_get_channel_cb, i, 1,
1464 3, channel_name, strlen(channel_name))) {
1466 completion(client, conn, SILC_STATUS_ERR_RESOURCE_LIMIT, NULL, context);
1470 /* Resolves channel information from the server by the channel ID. */
1473 silc_client_get_channel_by_id_resolve(SilcClient client,
1474 SilcClientConnection conn,
1475 SilcChannelID *channel_id,
1476 SilcGetChannelCallback completion,
1479 SilcClientGetChannelInternal i;
1481 SilcUInt16 cmd_ident;
1483 if (!client || !conn || !channel_id || !completion)
1486 SILC_LOG_DEBUG(("Resolve channel by id %s",
1487 silc_id_render(channel_id, SILC_ID_CHANNEL)));
1489 i = silc_calloc(1, sizeof(*i));
1492 i->completion = completion;
1493 i->context = context;
1494 i->channels = silc_dlist_init();
1500 /* Send the command */
1501 idp = silc_id_payload_encode(channel_id, SILC_ID_CHANNEL);
1502 cmd_ident = silc_client_command_send(client, conn, SILC_COMMAND_IDENTIFY,
1503 silc_client_get_channel_cb, i, 1,
1504 5, silc_buffer_datalen(idp));
1505 silc_buffer_free(idp);
1506 if (!cmd_ident && completion)
1507 completion(client, conn, SILC_STATUS_ERR_RESOURCE_LIMIT, NULL, context);
1512 /************************* Channel Entry Routines ***************************/
1514 /* Add new channel entry to the ID Cache */
1516 SilcChannelEntry silc_client_add_channel(SilcClient client,
1517 SilcClientConnection conn,
1518 const char *channel_name,
1520 SilcChannelID *channel_id)
1522 SilcChannelEntry channel;
1523 char *channel_namec;
1525 SILC_LOG_DEBUG(("Start"));
1527 channel = silc_calloc(1, sizeof(*channel));
1531 silc_rwlock_alloc(&channel->internal.lock);
1532 silc_atomic_init16(&channel->internal.refcnt, 0);
1533 channel->id = *channel_id;
1534 channel->mode = mode;
1536 channel->channel_name = strdup(channel_name);
1537 if (!channel->channel_name) {
1542 channel->user_list = silc_hash_table_alloc(1, silc_hash_ptr, NULL, NULL,
1543 NULL, NULL, NULL, TRUE);
1544 if (!channel->user_list) {
1545 silc_free(channel->channel_name);
1550 /* Normalize channel name */
1551 channel_namec = silc_channel_name_check(channel_name, strlen(channel_name),
1552 SILC_STRING_UTF8, 256, NULL);
1553 if (!channel_namec) {
1554 silc_free(channel->channel_name);
1555 silc_hash_table_free(channel->user_list);
1560 silc_mutex_lock(conn->internal->lock);
1562 /* Add channel to cache, the normalized channel name is saved to cache */
1563 if (!silc_idcache_add(conn->internal->channel_cache, channel_namec,
1564 &channel->id, channel)) {
1565 silc_free(channel_namec);
1566 silc_free(channel->channel_name);
1567 silc_hash_table_free(channel->user_list);
1569 silc_mutex_unlock(conn->internal->lock);
1573 silc_mutex_unlock(conn->internal->lock);
1574 silc_client_ref_channel(client, conn, channel);
1576 SILC_LOG_DEBUG(("Added %p", channel));
1581 /* Removes channel from the cache by the channel entry. */
1583 SilcBool silc_client_del_channel(SilcClient client, SilcClientConnection conn,
1584 SilcChannelEntry channel)
1586 SilcIDCacheEntry id_cache;
1594 if (silc_atomic_sub_int16(&channel->internal.refcnt, 1) > 0)
1597 SILC_LOG_DEBUG(("Deleting channel %p", channel));
1599 silc_mutex_lock(conn->internal->lock);
1600 if (silc_idcache_find_by_context(conn->internal->channel_cache, channel,
1602 silc_free(id_cache->name);
1603 ret = silc_idcache_del_by_context(conn->internal->channel_cache,
1605 silc_mutex_unlock(conn->internal->lock);
1610 silc_client_empty_channel(client, conn, channel);
1611 silc_hash_table_free(channel->user_list);
1612 silc_free(channel->channel_name);
1613 silc_free(channel->topic);
1614 if (channel->founder_key)
1615 silc_pkcs_public_key_free(channel->founder_key);
1616 if (channel->internal.send_key)
1617 silc_cipher_free(channel->internal.send_key);
1618 if (channel->internal.receive_key)
1619 silc_cipher_free(channel->internal.receive_key);
1620 if (channel->internal.hmac)
1621 silc_hmac_free(channel->internal.hmac);
1622 if (channel->internal.old_channel_keys) {
1623 silc_dlist_start(channel->internal.old_channel_keys);
1624 while ((key = silc_dlist_get(channel->internal.old_channel_keys)))
1625 silc_cipher_free(key);
1626 silc_dlist_uninit(channel->internal.old_channel_keys);
1628 if (channel->internal.old_hmacs) {
1629 silc_dlist_start(channel->internal.old_hmacs);
1630 while ((hmac = silc_dlist_get(channel->internal.old_hmacs)))
1631 silc_hmac_free(hmac);
1632 silc_dlist_uninit(channel->internal.old_hmacs);
1634 if (channel->channel_pubkeys)
1635 silc_argument_list_free(channel->channel_pubkeys,
1636 SILC_ARGUMENT_PUBLIC_KEY);
1637 silc_client_del_channel_private_keys(client, conn, channel);
1638 silc_atomic_uninit16(&channel->internal.refcnt);
1639 silc_rwlock_free(channel->internal.lock);
1640 silc_schedule_task_del_by_context(conn->client->schedule, channel);
1646 /* Replaces the channel ID of the `channel' to `new_id'. Returns FALSE
1647 if the ID could not be changed. This handles entry locking internally. */
1649 SilcBool silc_client_replace_channel_id(SilcClient client,
1650 SilcClientConnection conn,
1651 SilcChannelEntry channel,
1652 SilcChannelID *new_id)
1654 SilcBool ret = FALSE;
1659 SILC_LOG_DEBUG(("Old Channel ID id(%s)",
1660 silc_id_render(&channel->id, SILC_ID_CHANNEL)));
1661 SILC_LOG_DEBUG(("New Channel ID id(%s)",
1662 silc_id_render(new_id, SILC_ID_CHANNEL)));
1665 silc_rwlock_wrlock(channel->internal.lock);
1666 silc_mutex_lock(conn->internal->lock);
1667 silc_idcache_update_by_context(conn->internal->channel_cache, channel,
1668 new_id, NULL, FALSE);
1669 silc_mutex_unlock(conn->internal->lock);
1670 silc_rwlock_unlock(channel->internal.lock);
1677 void silc_client_lock_channel(SilcChannelEntry channel_entry)
1679 silc_rwlock_rdlock(channel_entry->internal.lock);
1682 /* Unlock channel */
1684 void silc_client_unlock_channel(SilcChannelEntry channel_entry)
1686 silc_rwlock_unlock(channel_entry->internal.lock);
1689 /* Take reference of channel entry */
1691 SilcChannelEntry silc_client_ref_channel(SilcClient client,
1692 SilcClientConnection conn,
1693 SilcChannelEntry channel_entry)
1695 silc_atomic_add_int16(&channel_entry->internal.refcnt, 1);
1696 SILC_LOG_DEBUG(("Channel %p refcnt %d->%d", channel_entry,
1697 silc_atomic_get_int16(&channel_entry->internal.refcnt) - 1,
1698 silc_atomic_get_int16(&channel_entry->internal.refcnt)));
1699 return channel_entry;
1702 /* Release reference of channel entry */
1704 void silc_client_unref_channel(SilcClient client, SilcClientConnection conn,
1705 SilcChannelEntry channel_entry)
1707 if (channel_entry) {
1708 SILC_LOG_DEBUG(("Channel %p refcnt %d->%d", channel_entry,
1709 silc_atomic_get_int16(&channel_entry->internal.refcnt),
1710 silc_atomic_get_int16(&channel_entry->internal.refcnt)
1712 silc_client_del_channel(client, conn, channel_entry);
1716 /* Free channel entry list */
1718 void silc_client_list_free_channels(SilcClient client,
1719 SilcClientConnection conn,
1720 SilcDList channel_list)
1722 SilcChannelEntry channel_entry;
1725 silc_dlist_start(channel_list);
1726 while ((channel_entry = silc_dlist_get(channel_list)))
1727 silc_client_unref_channel(client, conn, channel_entry);
1729 silc_dlist_uninit(channel_list);
1733 /************************* Server Searching Locally *************************/
1735 /* Finds entry for server by the server name. */
1737 SilcServerEntry silc_client_get_server(SilcClient client,
1738 SilcClientConnection conn,
1741 SilcIDCacheEntry id_cache;
1742 SilcServerEntry entry;
1744 if (!client || !conn || !server_name)
1747 SILC_LOG_DEBUG(("Find server by name %s", server_name));
1749 /* Normalize server name for search */
1750 server_name = silc_identifier_check(server_name, strlen(server_name),
1751 SILC_STRING_UTF8, 256, NULL);
1755 silc_mutex_lock(conn->internal->lock);
1757 if (!silc_idcache_find_by_name_one(conn->internal->server_cache,
1758 server_name, &id_cache)) {
1759 silc_free(server_name);
1760 silc_mutex_unlock(conn->internal->lock);
1764 SILC_LOG_DEBUG(("Found"));
1767 entry = id_cache->context;
1768 silc_client_ref_server(client, conn, entry);
1770 silc_mutex_unlock(conn->internal->lock);
1772 silc_free(server_name);
1777 /* Finds entry for server by the server ID. */
1779 SilcServerEntry silc_client_get_server_by_id(SilcClient client,
1780 SilcClientConnection conn,
1781 SilcServerID *server_id)
1783 SilcIDCacheEntry id_cache;
1784 SilcServerEntry entry;
1786 if (!client || !conn || !server_id)
1789 SILC_LOG_DEBUG(("Find server by id %s",
1790 silc_id_render(server_id, SILC_ID_SERVER)));
1792 silc_mutex_lock(conn->internal->lock);
1794 if (!silc_idcache_find_by_id_one(conn->internal->server_cache,
1795 server_id, &id_cache)) {
1796 silc_mutex_unlock(conn->internal->lock);
1800 SILC_LOG_DEBUG(("Found"));
1803 entry = id_cache->context;
1804 silc_client_ref_server(client, conn, entry);
1806 silc_mutex_unlock(conn->internal->lock);
1811 /*********************** Server Resolving from Server ***********************/
1813 /* Resolving context */
1816 SilcGetServerCallback completion;
1818 } *SilcClientGetServerInternal;
1820 /* Resolving command callback */
1822 static SilcBool silc_client_get_server_cb(SilcClient client,
1823 SilcClientConnection conn,
1824 SilcCommand command,
1830 SilcClientGetServerInternal i = context;
1831 SilcServerEntry server;
1833 if (error != SILC_STATUS_OK) {
1834 SILC_LOG_DEBUG(("Resolving failed: %s", silc_get_status_message(error)));
1836 i->completion(client, conn, error, NULL, i->context);
1840 /* Add the returned servers to list */
1841 if (i->completion) {
1842 server = va_arg(ap, SilcServerEntry);
1843 silc_client_ref_server(client, conn, server);
1844 silc_dlist_add(i->servers, server);
1845 server->internal.resolve_cmd_ident = 0;
1848 if (status == SILC_STATUS_OK || status == SILC_STATUS_LIST_END) {
1849 /* Deliver the servers to the caller */
1850 if (i->completion) {
1851 SILC_LOG_DEBUG(("Resolved %d servers", silc_dlist_count(i->servers)));
1852 silc_dlist_start(i->servers);
1853 i->completion(client, conn, SILC_STATUS_OK, i->servers, i->context);
1861 silc_client_list_free_servers(client, conn, i->servers);
1866 /* Resolve server by server ID */
1869 silc_client_get_server_by_id_resolve(SilcClient client,
1870 SilcClientConnection conn,
1871 SilcServerID *server_id,
1872 SilcGetServerCallback completion,
1875 SilcClientGetServerInternal i;
1876 SilcServerEntry server;
1878 SilcUInt16 cmd_ident;
1880 if (!client || !conn || !server_id || !completion)
1883 SILC_LOG_DEBUG(("Resolve server by id %s",
1884 silc_id_render(server_id, SILC_ID_SERVER)));
1886 i = silc_calloc(1, sizeof(*i));
1889 i->completion = completion;
1890 i->context = context;
1891 i->servers = silc_dlist_init();
1897 /* Attach to resolving, if on going */
1898 server = silc_client_get_server_by_id(client, conn, server_id);
1899 if (server && server->internal.resolve_cmd_ident) {
1900 SILC_LOG_DEBUG(("Attach to existing resolving"));
1901 silc_client_unref_server(client, conn, server);
1902 silc_client_command_pending(conn, SILC_COMMAND_NONE,
1903 server->internal.resolve_cmd_ident,
1904 silc_client_get_server_cb, i);
1905 return server->internal.resolve_cmd_ident;
1908 /* Send the command */
1909 idp = silc_id_payload_encode(server_id, SILC_ID_SERVER);
1910 cmd_ident = silc_client_command_send(client, conn, SILC_COMMAND_IDENTIFY,
1911 silc_client_get_server_cb, i, 1,
1912 5, silc_buffer_datalen(idp));
1913 silc_buffer_free(idp);
1914 if (!cmd_ident && completion)
1915 completion(client, conn, SILC_STATUS_ERR_RESOURCE_LIMIT, NULL, context);
1917 if (server && cmd_ident)
1918 server->internal.resolve_cmd_ident = cmd_ident;
1920 silc_client_unref_server(client, conn, server);
1925 /************************** Server Entry Routines ***************************/
1927 /* Add new server entry */
1929 SilcServerEntry silc_client_add_server(SilcClient client,
1930 SilcClientConnection conn,
1931 const char *server_name,
1932 const char *server_info,
1933 SilcServerID *server_id)
1935 SilcServerEntry server_entry;
1936 char *server_namec = NULL;
1941 SILC_LOG_DEBUG(("Adding new server %s", server_name));
1943 server_entry = silc_calloc(1, sizeof(*server_entry));
1947 silc_rwlock_alloc(&server_entry->internal.lock);
1948 silc_atomic_init8(&server_entry->internal.refcnt, 0);
1949 server_entry->id = *server_id;
1951 server_entry->server_name = strdup(server_name);
1953 server_entry->server_info = strdup(server_info);
1955 /* Normalize server name */
1957 server_namec = silc_identifier_check(server_name, strlen(server_name),
1958 SILC_STRING_UTF8, 256, NULL);
1959 if (!server_namec) {
1960 silc_free(server_entry->server_name);
1961 silc_free(server_entry->server_info);
1962 silc_free(server_entry);
1967 silc_mutex_lock(conn->internal->lock);
1969 /* Add server to cache */
1970 if (!silc_idcache_add(conn->internal->server_cache, server_namec,
1971 &server_entry->id, server_entry)) {
1972 silc_free(server_namec);
1973 silc_free(server_entry->server_name);
1974 silc_free(server_entry->server_info);
1975 silc_free(server_entry);
1976 silc_mutex_unlock(conn->internal->lock);
1980 silc_mutex_unlock(conn->internal->lock);
1981 silc_client_ref_server(client, conn, server_entry);
1983 SILC_LOG_DEBUG(("Added %p", server_entry));
1985 return server_entry;
1988 /* Removes server from the cache by the server entry. */
1990 SilcBool silc_client_del_server(SilcClient client, SilcClientConnection conn,
1991 SilcServerEntry server)
1993 SilcIDCacheEntry id_cache;
1999 if (silc_atomic_sub_int8(&server->internal.refcnt, 1) > 0)
2002 SILC_LOG_DEBUG(("Deleting server %p", server));
2004 silc_mutex_lock(conn->internal->lock);
2005 if (silc_idcache_find_by_context(conn->internal->server_cache, server,
2007 silc_free(id_cache->name);
2008 ret = silc_idcache_del_by_context(conn->internal->server_cache,
2010 silc_mutex_unlock(conn->internal->lock);
2012 silc_free(server->server_name);
2013 silc_free(server->server_info);
2014 if (server->public_key)
2015 silc_pkcs_public_key_free(server->public_key);
2016 silc_atomic_uninit8(&server->internal.refcnt);
2017 silc_rwlock_free(server->internal.lock);
2023 /* Updates the `server_entry' with the new information sent as argument. */
2025 void silc_client_update_server(SilcClient client,
2026 SilcClientConnection conn,
2027 SilcServerEntry server_entry,
2028 const char *server_name,
2029 const char *server_info)
2031 char *server_namec = NULL;
2033 SILC_LOG_DEBUG(("Updating server %p", server_entry));
2036 (!server_entry->server_name ||
2037 !silc_utf8_strcasecmp(server_entry->server_name, server_name))) {
2039 server_namec = silc_identifier_check(server_name, strlen(server_name),
2040 SILC_STRING_UTF8, 256, NULL);
2044 silc_free(server_entry->server_name);
2045 server_entry->server_name = strdup(server_name);
2046 if (!server_entry->server_name)
2049 /* Update cache entry */
2050 silc_mutex_lock(conn->internal->lock);
2051 silc_idcache_update_by_context(conn->internal->server_cache, server_entry,
2052 NULL, server_namec, TRUE);
2053 silc_mutex_unlock(conn->internal->lock);
2057 (!server_entry->server_info ||
2058 !silc_utf8_strcasecmp(server_entry->server_info, server_info))) {
2059 silc_free(server_entry->server_info);
2060 server_entry->server_info = strdup(server_info);
2066 void silc_client_lock_server(SilcServerEntry server_entry)
2068 silc_rwlock_rdlock(server_entry->internal.lock);
2073 void silc_client_unlock_server(SilcServerEntry server_entry)
2075 silc_rwlock_unlock(server_entry->internal.lock);
2078 /* Take reference of server entry */
2080 SilcServerEntry silc_client_ref_server(SilcClient client,
2081 SilcClientConnection conn,
2082 SilcServerEntry server_entry)
2084 silc_atomic_add_int8(&server_entry->internal.refcnt, 1);
2085 SILC_LOG_DEBUG(("Server %p refcnt %d->%d", server_entry,
2086 silc_atomic_get_int8(&server_entry->internal.refcnt) - 1,
2087 silc_atomic_get_int8(&server_entry->internal.refcnt)));
2088 return server_entry;
2091 /* Release reference of server entry */
2093 void silc_client_unref_server(SilcClient client, SilcClientConnection conn,
2094 SilcServerEntry server_entry)
2097 SILC_LOG_DEBUG(("Server %p refcnt %d->%d", server_entry,
2098 silc_atomic_get_int8(&server_entry->internal.refcnt),
2099 silc_atomic_get_int8(&server_entry->internal.refcnt)
2101 silc_client_del_server(client, conn, server_entry);
2105 /* Free server entry list */
2107 void silc_client_list_free_servers(SilcClient client,
2108 SilcClientConnection conn,
2109 SilcDList server_list)
2111 SilcServerEntry server_entry;
2114 silc_dlist_start(server_list);
2115 while ((server_entry = silc_dlist_get(server_list)))
2116 silc_client_unref_server(client, conn, server_entry);
2118 silc_dlist_uninit(server_list);