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 nick[128 + 1], *nicknamec, *parsed = NULL, *format = NULL;
78 if (!client || !conn || !nickname)
81 /* Get nickname from nickname@server string */
82 silc_parse_userfqdn(nickname, nick, sizeof(nick), server, sizeof(server));
84 /* Parse nickname in case it is formatted */
85 if (!silc_client_nickname_parse(client, conn, (char *)nick, &parsed))
88 if (!get_all && parsed)
89 format = (char *)nick;
91 parsed = silc_memdup(nick, strlen(nick));
96 SILC_LOG_DEBUG(("Find clients by nickname %s", parsed));
98 /* Normalize nickname for search */
99 nicknamec = silc_identifier_check(parsed, strlen(parsed),
100 SILC_STRING_UTF8, 128, NULL);
106 clients = silc_dlist_init();
108 silc_free(nicknamec);
113 silc_mutex_lock(conn->internal->lock);
115 /* Find from cache */
116 silc_list_init(list, struct SilcIDCacheEntryStruct, next);
117 if (!silc_idcache_find_by_name(conn->internal->client_cache, nicknamec,
119 silc_mutex_unlock(conn->internal->lock);
120 silc_free(nicknamec);
122 silc_dlist_uninit(clients);
125 silc_list_start(list);
127 if (!format && get_all) {
128 /* Take all without any further checking */
129 while ((id_cache = silc_list_get(list))) {
130 entry = id_cache->context;
131 if (!get_valid || entry->internal.valid) {
132 silc_client_ref_client(client, conn, id_cache->context);
133 silc_dlist_add(clients, id_cache->context);
137 /* Check multiple cache entries for exact match */
138 while ((id_cache = silc_list_get(list))) {
139 entry = id_cache->context;
141 /* If server was provided, find entries that either have no server
142 set or have the same server. Ignore those that have different
144 if (server[0] && entry->server &&
145 !silc_utf8_strcasecmp(entry->server, server))
148 if (silc_utf8_strcasecmp(entry->nickname,
149 format ? format : parsed) &&
150 (!get_valid || entry->internal.valid)) {
151 silc_client_ref_client(client, conn, entry);
152 silc_dlist_add(clients, entry);
154 /* If format is NULL, we find one exact match with the base
155 nickname (parsed). */
162 silc_mutex_unlock(conn->internal->lock);
164 silc_free(nicknamec);
167 if (!silc_dlist_count(clients)) {
168 silc_dlist_uninit(clients);
172 SILC_LOG_DEBUG(("Found %d clients", silc_dlist_count(clients)));
174 silc_dlist_start(clients);
178 /* Finds clients by nickname from local cache. */
180 SilcDList silc_client_get_clients_local(SilcClient client,
181 SilcClientConnection conn,
182 const char *nickname,
185 return silc_client_get_clients_local_ext(client, conn, nickname, return_all,
189 /********************** Client Resolving from Server ************************/
191 /* Resolving context */
194 SilcGetClientCallback completion;
196 SilcClientEntry client_entry;
197 } *SilcClientGetClientInternal;
199 /* Resolving command callback */
201 static SilcBool silc_client_get_clients_cb(SilcClient client,
202 SilcClientConnection conn,
209 SilcClientGetClientInternal i = context;
210 SilcClientEntry client_entry;
212 if (error != SILC_STATUS_OK) {
213 SILC_LOG_DEBUG(("Resolving failed: %s", silc_get_status_message(error)));
215 if (i->client_entry) {
216 i->client_entry->internal.resolve_cmd_ident = 0;
217 silc_client_unref_client(client, conn, i->client_entry);
221 i->completion(client, conn, error, NULL, i->context);
225 /* Add the returned client to list */
227 client_entry = va_arg(ap, SilcClientEntry);
228 silc_client_ref_client(client, conn, client_entry);
229 silc_dlist_add(i->clients, client_entry);
230 client_entry->internal.resolve_cmd_ident = 0;
233 if (status == SILC_STATUS_OK || status == SILC_STATUS_LIST_END) {
234 /* Deliver the clients to the caller */
236 SILC_LOG_DEBUG(("Resolved %d clients", silc_dlist_count(i->clients)));
238 if (i->client_entry) {
239 i->client_entry->internal.resolve_cmd_ident = 0;
240 silc_client_unref_client(client, conn, i->client_entry);
243 silc_dlist_start(i->clients);
244 i->completion(client, conn, SILC_STATUS_OK, i->clients, i->context);
252 silc_client_list_free(client, conn, i->clients);
257 /* Resolves client information from server by the client ID. */
260 silc_client_get_client_by_id_resolve(SilcClient client,
261 SilcClientConnection conn,
262 SilcClientID *client_id,
263 SilcBuffer attributes,
264 SilcGetClientCallback completion,
267 SilcClientGetClientInternal i;
268 SilcClientEntry client_entry;
270 SilcUInt16 cmd_ident;
272 if (!client || !conn | !client_id) {
273 SILC_LOG_ERROR(("Missing arguments to "
274 "silc_client_get_clients_by_id_resolve call"));
278 SILC_LOG_DEBUG(("Resolve client by ID (%s)",
279 silc_id_render(client_id, SILC_ID_CLIENT)));
281 i = silc_calloc(1, sizeof(*i));
284 i->completion = completion;
285 i->context = context;
286 i->clients = silc_dlist_init();
292 /* Attach to resolving, if on going */
293 client_entry = silc_client_get_client_by_id(client, conn, client_id);
294 if (client_entry && client_entry->internal.resolve_cmd_ident) {
295 SILC_LOG_DEBUG(("Attach to existing resolving"));
296 silc_client_unref_client(client, conn, client_entry);
297 silc_client_command_pending(conn, SILC_COMMAND_NONE,
298 client_entry->internal.resolve_cmd_ident,
299 silc_client_get_clients_cb, i);
300 return client_entry->internal.resolve_cmd_ident;
303 /* Send the command */
304 idp = silc_id_payload_encode(client_id, SILC_ID_CLIENT);
305 cmd_ident = silc_client_command_send(client, conn, SILC_COMMAND_WHOIS,
306 silc_client_get_clients_cb, i,
307 2, 3, silc_buffer_datalen(attributes),
308 4, silc_buffer_datalen(idp));
309 if (!cmd_ident && completion)
310 completion(client, conn, SILC_STATUS_ERR_RESOURCE_LIMIT, NULL, context);
312 if (client_entry && cmd_ident) {
313 client_entry->internal.resolve_cmd_ident = cmd_ident;
314 i->client_entry = client_entry;
316 silc_client_unref_client(client, conn, client_entry);
319 silc_buffer_free(idp);
324 /* Finds client entry or entries by the `nickname' and `server'. The
325 completion callback will be called when the client entries has been
326 found. Used internally by the library. */
328 static SilcUInt16 silc_client_get_clients_i(SilcClient client,
329 SilcClientConnection conn,
331 const char *nickname,
333 SilcBuffer attributes,
334 SilcGetClientCallback completion,
337 SilcClientGetClientInternal i;
338 char nick[128 + 1], serv[256 + 1], userhost[768 + 1], *parsed = NULL;
341 SILC_LOG_DEBUG(("Resolve client by %s command",
342 silc_get_command_name(command)));
344 if (!client || !conn) {
345 SILC_LOG_ERROR(("Missing arguments to silc_client_get_clients call"));
348 if (!nickname && !attributes) {
349 SILC_LOG_ERROR(("Missing arguments to silc_client_get_clients call"));
353 /* Parse server name from the nickname if set */
354 if (silc_parse_userfqdn(nickname, nick, sizeof(nick),
355 serv, sizeof(serv)) == 2)
356 server = (const char *)serv;
357 nickname = (const char *)nick;
359 /* Parse nickname in case it is formatted */
360 if (silc_client_nickname_parse(client, conn, (char *)nickname, &parsed))
361 nickname = (const char *)parsed;
363 i = silc_calloc(1, sizeof(*i));
368 i->clients = silc_dlist_init();
374 i->completion = completion;
375 i->context = context;
377 memset(userhost, 0, sizeof(userhost));
378 if (nickname && server) {
379 len = strlen(nickname) + strlen(server) + 3;
380 silc_strncat(userhost, len, nickname, strlen(nickname));
381 silc_strncat(userhost, len, "@", 1);
382 silc_strncat(userhost, len, server, strlen(server));
383 } else if (nickname) {
384 silc_strncat(userhost, sizeof(userhost) - 1, nickname, strlen(nickname));
388 /* Send the command */
389 if (command == SILC_COMMAND_IDENTIFY)
390 return silc_client_command_send(client, conn, SILC_COMMAND_IDENTIFY,
391 silc_client_get_clients_cb, i,
392 1, 1, userhost, strlen(userhost));
393 return silc_client_command_send(client, conn, SILC_COMMAND_WHOIS,
394 silc_client_get_clients_cb, i,
395 2, 1, userhost, strlen(userhost),
396 3, silc_buffer_datalen(attributes));
399 /* Get clients from server with IDENTIFY command */
401 SilcUInt16 silc_client_get_clients(SilcClient client,
402 SilcClientConnection conn,
403 const char *nickname,
405 SilcGetClientCallback completion,
408 return silc_client_get_clients_i(client, conn, SILC_COMMAND_IDENTIFY,
409 nickname, server, NULL,
410 completion, context);
413 /* Get clients from server with WHOIS command */
415 SilcUInt16 silc_client_get_clients_whois(SilcClient client,
416 SilcClientConnection conn,
417 const char *nickname,
419 SilcBuffer attributes,
420 SilcGetClientCallback completion,
423 return silc_client_get_clients_i(client, conn, SILC_COMMAND_WHOIS,
424 nickname, server, attributes,
425 completion, context);
428 /* ID list resolving context */
430 SilcGetClientCallback completion;
432 SilcBuffer client_id_list;
433 SilcUInt32 list_count;
434 } *GetClientsByListInternal;
436 static SilcBool silc_client_get_clients_list_cb(SilcClient client,
437 SilcClientConnection conn,
444 GetClientsByListInternal i = context;
445 SilcClientEntry client_entry;
451 /* Process the list after all replies have been received */
452 if (status != SILC_STATUS_OK && !SILC_STATUS_IS_ERROR(status) &&
453 status != SILC_STATUS_LIST_END)
456 SILC_LOG_DEBUG(("Resolved all clients"));
458 clients = silc_dlist_init();
460 status = SILC_STATUS_ERR_RESOURCE_LIMIT;
464 for (c = 0; c < i->list_count; c++) {
466 SILC_GET16_MSB(idp_len, i->client_id_list->data + 2);
468 if (!silc_id_payload_parse_id(i->client_id_list->data, idp_len, &id)) {
469 status = SILC_STATUS_ERR_BAD_CLIENT_ID;
473 /* Get client entry */
474 client_entry = silc_client_get_client_by_id(client, conn, &id.u.client_id);
476 silc_dlist_add(clients, client_entry);
478 if (!silc_buffer_pull(i->client_id_list, idp_len)) {
479 status = SILC_STATUS_ERR_BAD_CLIENT_ID;
484 silc_dlist_start(clients);
485 status = SILC_STATUS_OK;
487 i->completion(client, conn, status, clients, i->context);
490 if (status != SILC_STATUS_OK && i->completion)
491 i->completion(client, conn, status, NULL, i->context);
493 silc_client_list_free(client, conn, clients);
494 silc_buffer_free(i->client_id_list);
500 /* Gets client entries by the list of client ID's `client_id_list'. This
501 always resolves those client ID's it does not know yet from the server
502 so this function might take a while. The `client_id_list' is a list
503 of ID Payloads added one after other. JOIN command reply and USERS
504 command reply for example returns this sort of list. The `completion'
505 will be called after the entries are available. */
507 SilcUInt16 silc_client_get_clients_by_list(SilcClient client,
508 SilcClientConnection conn,
509 SilcUInt32 list_count,
510 SilcBuffer client_id_list,
511 SilcGetClientCallback completion,
514 GetClientsByListInternal in;
515 SilcClientEntry entry;
516 unsigned char **res_argv = NULL;
517 SilcUInt32 *res_argv_lens = NULL, *res_argv_types = NULL, res_argc = 0;
518 SilcUInt16 idp_len, cmd_ident;
523 SILC_LOG_DEBUG(("Resolve clients from Client ID list"));
525 if (!client || !conn || !client_id_list)
528 in = silc_calloc(1, sizeof(*in));
531 in->completion = completion;
532 in->context = context;
533 in->list_count = list_count;
534 in->client_id_list = silc_buffer_copy(client_id_list);
535 if (!in->client_id_list)
538 for (i = 0; i < list_count; i++) {
540 SILC_GET16_MSB(idp_len, client_id_list->data + 2);
542 if (!silc_id_payload_parse_id(client_id_list->data, idp_len, &id))
545 /* Check if we have this client cached already. If we don't have the
546 entry or it has incomplete info, then resolve it from the server. */
547 entry = silc_client_get_client_by_id(client, conn, &id.u.client_id);
548 if (!entry || !entry->nickname[0] || !entry->username[0] ||
551 res_argv = silc_calloc(list_count, sizeof(*res_argv));
552 res_argv_lens = silc_calloc(list_count, sizeof(*res_argv_lens));
553 res_argv_types = silc_calloc(list_count, sizeof(*res_argv_types));
554 if (!res_argv || !res_argv_lens || !res_argv_types) {
555 silc_client_unref_client(client, conn, entry);
560 res_argv[res_argc] = client_id_list->data;
561 res_argv_lens[res_argc] = idp_len;
562 res_argv_types[res_argc] = res_argc + 4;
565 silc_client_unref_client(client, conn, entry);
567 if (!silc_buffer_pull(client_id_list, idp_len))
570 silc_buffer_start(client_id_list);
572 /* Query the unknown client information from server */
574 cmd_ident = silc_client_command_send_argv(client,
575 conn, SILC_COMMAND_WHOIS,
576 silc_client_get_clients_list_cb,
577 in, res_argc, res_argv,
581 silc_free(res_argv_lens);
582 silc_free(res_argv_types);
586 /* We have the clients in cache, get them and call the completion */
587 silc_client_get_clients_list_cb(client, conn, SILC_COMMAND_WHOIS,
588 SILC_STATUS_OK, SILC_STATUS_OK, in, tmp);
592 silc_buffer_free(in->client_id_list);
595 silc_free(res_argv_lens);
596 silc_free(res_argv_types);
603 SilcClientConnection conn;
604 SilcChannelID channel_id;
605 SilcGetClientCallback completion;
608 } *GetClientsByChannelInternal;
610 SILC_CLIENT_CMD_FUNC(get_clients_by_channel_cb)
612 GetClientsByChannelInternal i = context;
613 SilcClientEntry *clients = NULL;
614 SilcUInt32 clients_count = 0;
615 SilcBool found = FALSE;
616 SilcChannelEntry channel;
617 SilcHashTableList htl;
626 channel = silc_client_get_channel_by_id(i->client, i->conn, &i->channel_id);
627 if (channel && !silc_hash_table_count(channel->user_list)) {
628 clients = silc_calloc(silc_hash_table_count(channel->user_list),
630 silc_hash_table_list(channel->user_list, &htl);
631 while (silc_hash_table_get(&htl, NULL, (void *)&chu))
632 clients[clients_count++] = chu->client;
633 silc_hash_table_list_reset(&htl);
638 i->completion(i->client, i->conn, clients, clients_count, i->context);
641 i->completion(i->client, i->conn, NULL, 0, i->context);
647 /* Gets client entries by the channel entry indicated by `channel'. Thus,
648 it resolves the clients currently on that channel. */
650 void silc_client_get_clients_by_channel(SilcClient client,
651 SilcClientConnection conn,
652 SilcChannelEntry channel,
653 SilcGetClientCallback completion,
656 GetClientsByChannelInternal in;
657 SilcHashTableList htl;
659 SilcClientEntry entry;
660 unsigned char **res_argv = NULL;
661 SilcUInt32 *res_argv_lens = NULL, *res_argv_types = NULL, res_argc = 0;
663 SilcBool wait_res = FALSE;
665 assert(client && conn && channel);
667 SILC_LOG_DEBUG(("Start"));
669 in = silc_calloc(1, sizeof(*in));
672 in->channel_id = *channel->id;
673 in->completion = completion;
674 in->context = context;
676 /* If user list does not exist, send USERS command. */
677 if (!channel->user_list || !silc_hash_table_count(channel->user_list)) {
678 SILC_LOG_DEBUG(("Sending USERS"));
679 silc_client_command_register(client, SILC_COMMAND_USERS, NULL, NULL,
680 silc_client_command_reply_users_i, 0,
682 silc_client_command_send(client, conn, SILC_COMMAND_USERS,
683 conn->cmd_ident, 1, 2, channel->channel_name,
684 strlen(channel->channel_name));
685 silc_client_command_pending(conn, SILC_COMMAND_USERS, conn->cmd_ident,
686 silc_client_command_get_clients_by_channel_cb,
691 silc_hash_table_list(channel->user_list, &htl);
692 while (silc_hash_table_get(&htl, NULL, (void *)&chu)) {
695 /* If the entry has incomplete info, then resolve it from the server. */
696 if (!entry->nickname[0] || !entry->realname) {
697 if (entry->status & SILC_CLIENT_STATUS_RESOLVING) {
698 /* Attach to this resolving and wait until it finishes */
699 silc_client_command_pending(
700 conn, SILC_COMMAND_NONE,
701 entry->resolve_cmd_ident,
702 silc_client_command_get_clients_by_channel_cb,
708 entry->status |= SILC_CLIENT_STATUS_RESOLVING;
709 entry->resolve_cmd_ident = conn->cmd_ident + 1;
711 idp = silc_id_payload_encode(entry->id, SILC_ID_CLIENT);
713 /* No we don't have it, query it from the server. Assemble argument
714 table that will be sent for the WHOIS command later. */
715 res_argv = silc_realloc(res_argv, sizeof(*res_argv) *
717 res_argv_lens = silc_realloc(res_argv_lens, sizeof(*res_argv_lens) *
719 res_argv_types = silc_realloc(res_argv_types, sizeof(*res_argv_types) *
721 res_argv[res_argc] = silc_memdup(idp->data, idp->len);
722 res_argv_lens[res_argc] = idp->len;
723 res_argv_types[res_argc] = res_argc + 4;
726 silc_buffer_free(idp);
729 silc_hash_table_list_reset(&htl);
731 /* Query the client information from server if the list included clients
732 that we don't know about. */
736 /* Send the WHOIS command to server */
737 res_cmd = silc_command_payload_encode(SILC_COMMAND_WHOIS,
738 res_argc, res_argv, res_argv_lens,
739 res_argv_types, ++conn->cmd_ident);
740 silc_client_packet_send(client, conn->sock, SILC_PACKET_COMMAND,
741 NULL, 0, NULL, NULL, res_cmd->data, res_cmd->len,
744 /* Register our own command reply for this command */
745 silc_client_command_register(client, SILC_COMMAND_WHOIS, NULL, NULL,
746 silc_client_command_reply_whois_i, 0,
749 /* Process the applications request after reply has been received */
750 silc_client_command_pending(
751 conn, SILC_COMMAND_WHOIS, conn->cmd_ident,
752 silc_client_command_get_clients_by_channel_cb,
756 silc_buffer_free(res_cmd);
758 silc_free(res_argv_lens);
759 silc_free(res_argv_types);
766 /* We have the clients in cache, get them and call the completion */
767 silc_client_command_get_clients_by_channel_cb((void *)in, NULL);
772 /************************** Client Entry Routines ***************************/
774 /* Creates new client entry and adds it to the ID cache. Returns pointer
777 SilcClientEntry silc_client_add_client(SilcClient client,
778 SilcClientConnection conn,
779 char *nickname, char *username,
780 char *userinfo, SilcClientID *id,
783 SilcClientEntry client_entry;
786 SILC_LOG_DEBUG(("Adding new client entry"));
788 /* Save the client infos */
789 client_entry = silc_calloc(1, sizeof(*client_entry));
793 silc_rwlock_alloc(&client_entry->internal.lock);
794 silc_atomic_init8(&client_entry->internal.refcnt, 0);
795 client_entry->id = *id;
796 client_entry->mode = mode;
797 client_entry->realname = userinfo ? strdup(userinfo) : NULL;
798 silc_parse_userfqdn(nickname, client_entry->nickname,
799 sizeof(client_entry->nickname),
800 client_entry->server,
801 sizeof(client_entry->server));
802 silc_parse_userfqdn(username, client_entry->username,
803 sizeof(client_entry->username),
804 client_entry->hostname,
805 sizeof(client_entry->hostname));
806 client_entry->channels = silc_hash_table_alloc(1, silc_hash_ptr, NULL, NULL,
807 NULL, NULL, NULL, TRUE);
808 if (!client_entry->channels) {
809 silc_free(client_entry->realname);
810 silc_free(client_entry);
814 /* Normalize nickname */
815 if (client_entry->nickname[0]) {
816 nick = silc_identifier_check(client_entry->nickname,
817 strlen(client_entry->nickname),
818 SILC_STRING_UTF8, 128, NULL);
820 silc_free(client_entry->realname);
821 silc_hash_table_free(client_entry->channels);
822 silc_free(client_entry);
827 silc_mutex_lock(conn->internal->lock);
829 /* Add client to cache, the normalized nickname is saved to cache */
830 if (!silc_idcache_add(conn->internal->client_cache, nick,
831 &client_entry->id, client_entry)) {
833 silc_free(client_entry->realname);
834 silc_hash_table_free(client_entry->channels);
835 silc_free(client_entry);
836 silc_mutex_unlock(conn->internal->lock);
840 client_entry->nickname_normalized = nick;
842 silc_mutex_unlock(conn->internal->lock);
843 silc_client_ref_client(client, conn, client_entry);
845 /* Format the nickname */
846 silc_client_nickname_format(client, conn, client_entry, FALSE);
848 if (client_entry->nickname[0])
849 client_entry->internal.valid = TRUE;
851 SILC_LOG_DEBUG(("Added %p", client_entry));
856 /* Updates the `client_entry' with the new information sent as argument.
857 This handles entry locking internally. */
859 void silc_client_update_client(SilcClient client,
860 SilcClientConnection conn,
861 SilcClientEntry client_entry,
862 const char *nickname,
863 const char *username,
864 const char *userinfo,
869 SILC_LOG_DEBUG(("Update client entry"));
871 silc_rwlock_wrlock(client_entry->internal.lock);
873 if (!client_entry->realname && userinfo)
874 client_entry->realname = strdup(userinfo);
875 if ((!client_entry->username[0] || !client_entry->hostname[0]) && username)
876 silc_parse_userfqdn(username, client_entry->username,
877 sizeof(client_entry->username),
878 client_entry->hostname,
879 sizeof(client_entry->username));
880 if (!client_entry->nickname[0] && nickname) {
881 silc_parse_userfqdn(nickname, client_entry->nickname,
882 sizeof(client_entry->nickname),
883 client_entry->server,
884 sizeof(client_entry->server));
886 /* Normalize nickname */
887 nick = silc_identifier_check(client_entry->nickname,
888 strlen(client_entry->nickname),
889 SILC_STRING_UTF8, 128, NULL);
891 silc_rwlock_unlock(client_entry->internal.lock);
895 /* Format nickname */
896 silc_client_nickname_format(client, conn, client_entry,
897 client_entry == conn->local_entry);
899 /* Update cache entry */
900 silc_mutex_lock(conn->internal->lock);
901 silc_idcache_update_by_context(conn->internal->client_cache,
902 client_entry, NULL, nick, TRUE);
903 silc_mutex_unlock(conn->internal->lock);
904 client_entry->nickname_normalized = nick;
905 client_entry->internal.valid = TRUE;
907 client_entry->mode = mode;
909 silc_rwlock_unlock(client_entry->internal.lock);
912 /* Change a client's nickname. Must be called with `client_entry' locked. */
914 SilcBool silc_client_change_nickname(SilcClient client,
915 SilcClientConnection conn,
916 SilcClientEntry client_entry,
917 const char *new_nick,
918 SilcClientID *new_id,
919 const unsigned char *idp,
924 SILC_LOG_DEBUG(("Change nickname %s to %s", client_entry->nickname,
927 /* Normalize nickname */
928 tmp = silc_identifier_check(new_nick, strlen(new_nick),
929 SILC_STRING_UTF8, 128, NULL);
933 /* Update the client entry */
934 silc_mutex_lock(conn->internal->lock);
935 if (!silc_idcache_update_by_context(conn->internal->client_cache,
936 client_entry, new_id, tmp, TRUE)) {
938 silc_mutex_unlock(conn->internal->lock);
941 silc_mutex_unlock(conn->internal->lock);
943 memset(client_entry->nickname, 0, sizeof(client_entry->nickname));
944 memcpy(client_entry->nickname, new_nick, strlen(new_nick));
945 client_entry->nickname_normalized = tmp;
946 silc_client_nickname_format(client, conn, client_entry,
947 client_entry == conn->local_entry);
949 /* For my client entry, update ID and set new ID to packet stream */
950 if (client_entry == conn->local_entry) {
951 if (idp && idp_len) {
952 silc_buffer_enlarge(conn->internal->local_idp, idp_len);
953 silc_buffer_put(conn->internal->local_idp, idp, idp_len);
956 silc_packet_set_ids(conn->stream, SILC_ID_CLIENT, conn->local_id,
960 client_entry->internal.valid = TRUE;
964 /* Deletes the client entry and frees all memory. */
966 void silc_client_del_client_entry(SilcClient client,
967 SilcClientConnection conn,
968 SilcClientEntry client_entry)
970 silc_free(client_entry->realname);
971 silc_free(client_entry->nickname_normalized);
972 silc_free(client_entry->internal.key);
973 if (client_entry->public_key)
974 silc_pkcs_public_key_free(client_entry->public_key);
975 silc_hash_table_free(client_entry->channels);
976 if (client_entry->internal.send_key)
977 silc_cipher_free(client_entry->internal.send_key);
978 if (client_entry->internal.receive_key)
979 silc_cipher_free(client_entry->internal.receive_key);
980 if (client_entry->internal.hmac_send)
981 silc_hmac_free(client_entry->internal.hmac_send);
982 if (client_entry->internal.hmac_receive)
983 silc_hmac_free(client_entry->internal.hmac_receive);
984 silc_client_ftp_session_free_client(client, client_entry);
985 if (client_entry->internal.ke)
986 silc_client_abort_key_agreement(client, conn, client_entry);
987 silc_atomic_uninit8(&client_entry->internal.refcnt);
988 silc_rwlock_free(client_entry->internal.lock);
989 silc_free(client_entry);
992 /* Removes client from the cache by the client entry. */
994 SilcBool silc_client_del_client(SilcClient client, SilcClientConnection conn,
995 SilcClientEntry client_entry)
1002 if (silc_atomic_sub_int8(&client_entry->internal.refcnt, 1) > 0)
1005 SILC_LOG_DEBUG(("Deleting client %p", client_entry));
1007 silc_mutex_lock(conn->internal->lock);
1008 ret = silc_idcache_del_by_context(conn->internal->client_cache,
1009 client_entry, NULL);
1010 silc_mutex_unlock(conn->internal->lock);
1013 /* Remove from channels */
1014 silc_client_remove_from_channels(client, conn, client_entry);
1016 /* Free the client entry data */
1017 silc_client_del_client_entry(client, conn, client_entry);
1023 /* Internal routine used to find client by ID and if not found this creates
1024 new client entry and returns it. */
1026 SilcClientEntry silc_client_get_client(SilcClient client,
1027 SilcClientConnection conn,
1028 SilcClientID *client_id)
1030 SilcClientEntry client_entry;
1032 client_entry = silc_client_get_client_by_id(client, conn, client_id);
1033 if (!client_entry) {
1034 client_entry = silc_client_add_client(client, conn, NULL, NULL, NULL,
1038 silc_client_ref_client(client, conn, client_entry);
1041 return client_entry;
1046 void silc_client_lock_client(SilcClientEntry client_entry)
1048 silc_rwlock_rdlock(client_entry->internal.lock);
1053 void silc_client_unlock_client(SilcClientEntry client_entry)
1055 silc_rwlock_unlock(client_entry->internal.lock);
1058 /* Take reference of client entry */
1060 SilcClientEntry silc_client_ref_client(SilcClient client,
1061 SilcClientConnection conn,
1062 SilcClientEntry client_entry)
1064 silc_atomic_add_int8(&client_entry->internal.refcnt, 1);
1065 SILC_LOG_DEBUG(("Client %p refcnt %d->%d", client_entry,
1066 silc_atomic_get_int8(&client_entry->internal.refcnt) - 1,
1067 silc_atomic_get_int8(&client_entry->internal.refcnt)));
1068 return client_entry;
1071 /* Release reference of client entry */
1073 void silc_client_unref_client(SilcClient client, SilcClientConnection conn,
1074 SilcClientEntry client_entry)
1077 SILC_LOG_DEBUG(("Client %p refcnt %d->%d", client_entry,
1078 silc_atomic_get_int8(&client_entry->internal.refcnt),
1079 silc_atomic_get_int8(&client_entry->internal.refcnt) - 1));
1080 silc_client_del_client(client, conn, client_entry);
1084 /* Free client entry list */
1086 void silc_client_list_free(SilcClient client, SilcClientConnection conn,
1087 SilcDList client_list)
1089 SilcClientEntry client_entry;
1092 silc_dlist_start(client_list);
1093 while ((client_entry = silc_dlist_get(client_list)))
1094 silc_client_unref_client(client, conn, client_entry);
1096 silc_dlist_uninit(client_list);
1100 /* Formats the nickname of the client specified by the `client_entry'.
1101 If the format is specified by the application this will format the
1102 nickname and replace the old nickname in the client entry. If the
1103 format string is not specified then this function has no effect.
1104 Returns the client entry that was formatted. */
1106 SilcClientEntry silc_client_nickname_format(SilcClient client,
1107 SilcClientConnection conn,
1108 SilcClientEntry client_entry,
1112 char newnick[128 + 1];
1113 int i, off = 0, len;
1115 SilcClientEntry entry, unformatted = NULL;
1116 SilcBool formatted = FALSE;
1118 if (!client->internal->params->nickname_format[0])
1119 return client_entry;
1120 if (!client_entry->nickname[0])
1123 SILC_LOG_DEBUG(("Format nickname"));
1125 /* Get all clients with same nickname. Do not perform the formatting
1126 if there aren't any clients with same nickname unless the application
1127 is forcing us to do so. */
1128 clients = silc_client_get_clients_local_ext(client, conn,
1129 client_entry->nickname,
1133 if (silc_dlist_count(clients) == 1 && !priority &&
1134 !client->internal->params->nickname_force_format) {
1135 silc_client_list_free(client, conn, clients);
1136 return client_entry;
1139 /* Is the requested client formatted already */
1140 if (client_entry->nickname_normalized &&
1141 !silc_utf8_strcasecmp(client_entry->nickname,
1142 client_entry->nickname_normalized))
1145 if (client->internal->params->nickname_force_format)
1148 /* Find unformatted client entry */
1149 while ((entry = silc_dlist_get(clients))) {
1150 if (!entry->internal.valid)
1152 if (entry == client_entry)
1154 if (silc_utf8_strcasecmp(entry->nickname, entry->nickname_normalized)) {
1155 unformatted = entry;
1160 /* If there are no other unformatted clients and the requested client is
1161 unformatted, just return it. */
1162 if (!unformatted && !formatted) {
1163 silc_client_list_free(client, conn, clients);
1164 return client_entry;
1167 /* If priority formatting then the requested client will get the
1168 unformatted nickname, and the unformatted client will get a new
1169 formatted nickname. */
1172 /* Simply change the client's nickname to unformatted */
1173 if (!silc_client_nickname_parse(client, conn, client_entry->nickname,
1177 silc_snprintf(client_entry->nickname, sizeof(client_entry->nickname),
1183 /* There was no other unformatted client */
1184 silc_client_list_free(client, conn, clients);
1185 return client_entry;
1188 /* Now format the previously unformatted client */
1189 client_entry = unformatted;
1193 /* If already formatted just return it */
1195 silc_client_list_free(client, conn, clients);
1196 return client_entry;
1199 memset(newnick, 0, sizeof(newnick));
1200 cp = client->internal->params->nickname_format;
1210 if (!client_entry->nickname[0])
1212 len = strlen(client_entry->nickname);
1213 memcpy(&newnick[off], client_entry->nickname, len);
1217 /* Stripped hostname */
1218 if (!client_entry->hostname[0])
1220 len = strcspn(client_entry->hostname, ".");
1221 i = strcspn(client_entry->hostname, "-");
1224 memcpy(&newnick[off], client_entry->hostname, len);
1229 if (!client_entry->hostname[0])
1231 len = strlen(client_entry->hostname);
1232 memcpy(&newnick[off], client_entry->hostname, len);
1236 /* Ascending number */
1241 if (silc_dlist_count(clients) == 1)
1244 silc_dlist_start(clients);
1245 while ((entry = silc_dlist_get(clients))) {
1246 if (!silc_utf8_strncasecmp(entry->nickname, newnick, off))
1248 if (strlen(entry->nickname) <= off)
1250 num = atoi(&entry->nickname[off]);
1255 memset(tmp, 0, sizeof(tmp));
1256 silc_snprintf(tmp, sizeof(tmp) - 1, "%d", ++max);
1258 memcpy(&newnick[off], tmp, len);
1263 /* Some other character in the string */
1264 memcpy(&newnick[off], cp, 1);
1273 memcpy(client_entry->nickname, newnick, strlen(newnick));
1274 silc_client_list_free(client, conn, clients);
1276 return client_entry;
1279 /* Parses nickname according to nickname format string */
1281 SilcBool silc_client_nickname_parse(SilcClient client,
1282 SilcClientConnection conn,
1286 char *cp, s = 0, e = 0, *nick;
1290 if (!client->internal->params->nickname_format[0]) {
1295 if (!nickname || !nickname[0])
1298 cp = client->internal->params->nickname_format;
1316 /* Get separator character */
1329 /* Parse the nickname */
1333 if (strchr(nickname, s))
1334 nick = strchr(nickname, s) + 1;
1336 if (strchr(nick, e))
1337 len = strchr(nick, e) - nick;
1341 *ret_nick = silc_memdup(nick, len);
1345 SILC_LOG_DEBUG(("Parsed nickname: %s", *ret_nick));
1350 /************************ Channel Searching Locally *************************/
1352 /* Finds entry for channel by the channel name. Returns the entry or NULL
1353 if the entry was not found. It is found only if the client is joined
1356 SilcChannelEntry silc_client_get_channel(SilcClient client,
1357 SilcClientConnection conn,
1360 SilcIDCacheEntry id_cache;
1361 SilcChannelEntry entry;
1363 if (!client || !conn || !channel)
1366 SILC_LOG_DEBUG(("Find channel %s", channel));
1368 /* Normalize name for search */
1369 channel = silc_channel_name_check(channel, strlen(channel), SILC_STRING_UTF8,
1374 silc_mutex_lock(conn->internal->lock);
1376 if (!silc_idcache_find_by_name_one(conn->internal->channel_cache, channel,
1378 silc_mutex_unlock(conn->internal->lock);
1383 SILC_LOG_DEBUG(("Found"));
1385 entry = id_cache->context;
1388 silc_client_ref_channel(client, conn, entry);
1389 silc_mutex_unlock(conn->internal->lock);
1396 /* Finds entry for channel by the channel ID. Returns the entry or NULL
1397 if the entry was not found. It is found only if the client is joined
1400 SilcChannelEntry silc_client_get_channel_by_id(SilcClient client,
1401 SilcClientConnection conn,
1402 SilcChannelID *channel_id)
1404 SilcIDCacheEntry id_cache;
1405 SilcChannelEntry entry;
1407 if (!client || !conn || !channel_id)
1410 SILC_LOG_DEBUG(("Find channel by id %s",
1411 silc_id_render(channel_id, SILC_ID_CHANNEL)));
1413 silc_mutex_lock(conn->internal->lock);
1415 if (!silc_idcache_find_by_id_one(conn->internal->channel_cache, channel_id,
1417 silc_mutex_unlock(conn->internal->lock);
1421 SILC_LOG_DEBUG(("Found"));
1423 entry = id_cache->context;
1426 silc_client_ref_channel(client, conn, entry);
1427 silc_mutex_unlock(conn->internal->lock);
1432 /********************** Channel Resolving from Server ***********************/
1434 /* Channel resolving context */
1437 SilcGetChannelCallback completion;
1439 } *SilcClientGetChannelInternal;
1441 /* Resolving command callback */
1443 static SilcBool silc_client_get_channel_cb(SilcClient client,
1444 SilcClientConnection conn,
1445 SilcCommand command,
1451 SilcClientGetChannelInternal i = context;
1452 SilcChannelEntry entry;
1454 if (error != SILC_STATUS_OK) {
1455 SILC_LOG_DEBUG(("Resolving failed: %s", silc_get_status_message(error)));
1457 i->completion(client, conn, error, NULL, i->context);
1461 /* Add the returned channel to list */
1462 if (i->completion) {
1463 entry = va_arg(ap, SilcChannelEntry);
1464 silc_client_ref_channel(client, conn, entry);
1465 silc_dlist_add(i->channels, entry);
1468 if (status == SILC_STATUS_OK || status == SILC_STATUS_LIST_END) {
1469 /* Deliver the channels to the caller */
1470 if (i->completion) {
1471 SILC_LOG_DEBUG(("Resolved %d channels", silc_dlist_count(i->channels)));
1472 silc_dlist_start(i->channels);
1473 i->completion(client, conn, SILC_STATUS_OK, i->channels, i->context);
1481 silc_client_list_free_channels(client, conn, i->channels);
1486 /* Resolves channel entry from the server by the channel name. */
1488 void silc_client_get_channel_resolve(SilcClient client,
1489 SilcClientConnection conn,
1491 SilcGetChannelCallback completion,
1494 SilcClientGetChannelInternal i;
1496 if (!client || !conn || !channel_name || !completion)
1499 SILC_LOG_DEBUG(("Resolve channel %s", channel_name));
1501 i = silc_calloc(1, sizeof(*i));
1504 i->completion = completion;
1505 i->context = context;
1506 i->channels = silc_dlist_init();
1512 /* Send the command */
1513 if (!silc_client_command_send(client, conn, SILC_COMMAND_IDENTIFY,
1514 silc_client_get_channel_cb, i, 1,
1515 3, channel_name, strlen(channel_name))) {
1517 completion(client, conn, SILC_STATUS_ERR_RESOURCE_LIMIT, NULL, context);
1521 /* Resolves channel information from the server by the channel ID. */
1524 silc_client_get_channel_by_id_resolve(SilcClient client,
1525 SilcClientConnection conn,
1526 SilcChannelID *channel_id,
1527 SilcGetChannelCallback completion,
1530 SilcClientGetChannelInternal i;
1532 SilcUInt16 cmd_ident;
1534 if (!client || !conn || !channel_id || !completion)
1537 SILC_LOG_DEBUG(("Resolve channel by id %s",
1538 silc_id_render(channel_id, SILC_ID_CHANNEL)));
1540 i = silc_calloc(1, sizeof(*i));
1543 i->completion = completion;
1544 i->context = context;
1545 i->channels = silc_dlist_init();
1551 /* Send the command */
1552 idp = silc_id_payload_encode(channel_id, SILC_ID_CHANNEL);
1553 cmd_ident = silc_client_command_send(client, conn, SILC_COMMAND_IDENTIFY,
1554 silc_client_get_channel_cb, i, 1,
1555 5, silc_buffer_datalen(idp));
1556 silc_buffer_free(idp);
1557 if (!cmd_ident && completion)
1558 completion(client, conn, SILC_STATUS_ERR_RESOURCE_LIMIT, NULL, context);
1563 /************************* Channel Entry Routines ***************************/
1565 /* Add new channel entry to the ID Cache */
1567 SilcChannelEntry silc_client_add_channel(SilcClient client,
1568 SilcClientConnection conn,
1569 const char *channel_name,
1571 SilcChannelID *channel_id)
1573 SilcChannelEntry channel;
1574 char *channel_namec;
1576 SILC_LOG_DEBUG(("Start"));
1578 channel = silc_calloc(1, sizeof(*channel));
1582 silc_rwlock_alloc(&channel->internal.lock);
1583 silc_atomic_init16(&channel->internal.refcnt, 0);
1584 channel->id = *channel_id;
1585 channel->mode = mode;
1587 channel->channel_name = strdup(channel_name);
1588 if (!channel->channel_name) {
1593 channel->user_list = silc_hash_table_alloc(1, silc_hash_ptr, NULL, NULL,
1594 NULL, NULL, NULL, TRUE);
1595 if (!channel->user_list) {
1596 silc_free(channel->channel_name);
1601 /* Normalize channel name */
1602 channel_namec = silc_channel_name_check(channel_name, strlen(channel_name),
1603 SILC_STRING_UTF8, 256, NULL);
1604 if (!channel_namec) {
1605 silc_free(channel->channel_name);
1606 silc_hash_table_free(channel->user_list);
1611 silc_mutex_lock(conn->internal->lock);
1613 /* Add channel to cache, the normalized channel name is saved to cache */
1614 if (!silc_idcache_add(conn->internal->channel_cache, channel_namec,
1615 &channel->id, channel)) {
1616 silc_free(channel_namec);
1617 silc_free(channel->channel_name);
1618 silc_hash_table_free(channel->user_list);
1620 silc_mutex_unlock(conn->internal->lock);
1624 silc_mutex_unlock(conn->internal->lock);
1625 silc_client_ref_channel(client, conn, channel);
1627 SILC_LOG_DEBUG(("Added %p", channel));
1632 /* Removes channel from the cache by the channel entry. */
1634 SilcBool silc_client_del_channel(SilcClient client, SilcClientConnection conn,
1635 SilcChannelEntry channel)
1637 SilcIDCacheEntry id_cache;
1638 SilcBool ret = TRUE;
1646 if (silc_atomic_sub_int16(&channel->internal.refcnt, 1) > 0)
1649 SILC_LOG_DEBUG(("Deleting channel %p", channel));
1651 silc_mutex_lock(conn->internal->lock);
1652 if (silc_idcache_find_by_context(conn->internal->channel_cache, channel,
1654 namec = id_cache->name;
1655 ret = silc_idcache_del_by_context(conn->internal->channel_cache,
1659 silc_mutex_unlock(conn->internal->lock);
1664 silc_client_empty_channel(client, conn, channel);
1665 silc_hash_table_free(channel->user_list);
1666 silc_free(channel->channel_name);
1667 silc_free(channel->topic);
1668 if (channel->founder_key)
1669 silc_pkcs_public_key_free(channel->founder_key);
1670 if (channel->internal.send_key)
1671 silc_cipher_free(channel->internal.send_key);
1672 if (channel->internal.receive_key)
1673 silc_cipher_free(channel->internal.receive_key);
1674 if (channel->internal.hmac)
1675 silc_hmac_free(channel->internal.hmac);
1676 if (channel->internal.old_channel_keys) {
1677 silc_dlist_start(channel->internal.old_channel_keys);
1678 while ((key = silc_dlist_get(channel->internal.old_channel_keys)))
1679 silc_cipher_free(key);
1680 silc_dlist_uninit(channel->internal.old_channel_keys);
1682 if (channel->internal.old_hmacs) {
1683 silc_dlist_start(channel->internal.old_hmacs);
1684 while ((hmac = silc_dlist_get(channel->internal.old_hmacs)))
1685 silc_hmac_free(hmac);
1686 silc_dlist_uninit(channel->internal.old_hmacs);
1688 if (channel->channel_pubkeys)
1689 silc_argument_list_free(channel->channel_pubkeys,
1690 SILC_ARGUMENT_PUBLIC_KEY);
1691 silc_client_del_channel_private_keys(client, conn, channel);
1692 silc_atomic_uninit16(&channel->internal.refcnt);
1693 silc_rwlock_free(channel->internal.lock);
1694 silc_schedule_task_del_by_context(conn->client->schedule, channel);
1700 /* Replaces the channel ID of the `channel' to `new_id'. Returns FALSE
1701 if the ID could not be changed. This handles entry locking internally. */
1703 SilcBool silc_client_replace_channel_id(SilcClient client,
1704 SilcClientConnection conn,
1705 SilcChannelEntry channel,
1706 SilcChannelID *new_id)
1708 SilcBool ret = FALSE;
1713 SILC_LOG_DEBUG(("Old Channel ID id(%s)",
1714 silc_id_render(&channel->id, SILC_ID_CHANNEL)));
1715 SILC_LOG_DEBUG(("New Channel ID id(%s)",
1716 silc_id_render(new_id, SILC_ID_CHANNEL)));
1719 silc_rwlock_wrlock(channel->internal.lock);
1720 silc_mutex_lock(conn->internal->lock);
1721 silc_idcache_update_by_context(conn->internal->channel_cache, channel,
1722 new_id, NULL, FALSE);
1723 silc_mutex_unlock(conn->internal->lock);
1724 silc_rwlock_unlock(channel->internal.lock);
1731 void silc_client_lock_channel(SilcChannelEntry channel_entry)
1733 silc_rwlock_rdlock(channel_entry->internal.lock);
1736 /* Unlock channel */
1738 void silc_client_unlock_channel(SilcChannelEntry channel_entry)
1740 silc_rwlock_unlock(channel_entry->internal.lock);
1743 /* Take reference of channel entry */
1745 SilcChannelEntry silc_client_ref_channel(SilcClient client,
1746 SilcClientConnection conn,
1747 SilcChannelEntry channel_entry)
1749 silc_atomic_add_int16(&channel_entry->internal.refcnt, 1);
1750 SILC_LOG_DEBUG(("Channel %p refcnt %d->%d", channel_entry,
1751 silc_atomic_get_int16(&channel_entry->internal.refcnt) - 1,
1752 silc_atomic_get_int16(&channel_entry->internal.refcnt)));
1753 return channel_entry;
1756 /* Release reference of channel entry */
1758 void silc_client_unref_channel(SilcClient client, SilcClientConnection conn,
1759 SilcChannelEntry channel_entry)
1761 if (channel_entry) {
1762 SILC_LOG_DEBUG(("Channel %p refcnt %d->%d", channel_entry,
1763 silc_atomic_get_int16(&channel_entry->internal.refcnt),
1764 silc_atomic_get_int16(&channel_entry->internal.refcnt)
1766 silc_client_del_channel(client, conn, channel_entry);
1770 /* Free channel entry list */
1772 void silc_client_list_free_channels(SilcClient client,
1773 SilcClientConnection conn,
1774 SilcDList channel_list)
1776 SilcChannelEntry channel_entry;
1779 silc_dlist_start(channel_list);
1780 while ((channel_entry = silc_dlist_get(channel_list)))
1781 silc_client_unref_channel(client, conn, channel_entry);
1783 silc_dlist_uninit(channel_list);
1787 /************************* Server Searching Locally *************************/
1789 /* Finds entry for server by the server name. */
1791 SilcServerEntry silc_client_get_server(SilcClient client,
1792 SilcClientConnection conn,
1795 SilcIDCacheEntry id_cache;
1796 SilcServerEntry entry;
1798 if (!client || !conn || !server_name)
1801 SILC_LOG_DEBUG(("Find server by name %s", server_name));
1803 /* Normalize server name for search */
1804 server_name = silc_identifier_check(server_name, strlen(server_name),
1805 SILC_STRING_UTF8, 256, NULL);
1809 silc_mutex_lock(conn->internal->lock);
1811 if (!silc_idcache_find_by_name_one(conn->internal->server_cache,
1812 server_name, &id_cache)) {
1813 silc_free(server_name);
1814 silc_mutex_unlock(conn->internal->lock);
1818 SILC_LOG_DEBUG(("Found"));
1821 entry = id_cache->context;
1822 silc_client_ref_server(client, conn, entry);
1824 silc_mutex_unlock(conn->internal->lock);
1826 silc_free(server_name);
1831 /* Finds entry for server by the server ID. */
1833 SilcServerEntry silc_client_get_server_by_id(SilcClient client,
1834 SilcClientConnection conn,
1835 SilcServerID *server_id)
1837 SilcIDCacheEntry id_cache;
1838 SilcServerEntry entry;
1840 if (!client || !conn || !server_id)
1843 SILC_LOG_DEBUG(("Find server by id %s",
1844 silc_id_render(server_id, SILC_ID_SERVER)));
1846 silc_mutex_lock(conn->internal->lock);
1848 if (!silc_idcache_find_by_id_one(conn->internal->server_cache,
1849 server_id, &id_cache)) {
1850 silc_mutex_unlock(conn->internal->lock);
1854 SILC_LOG_DEBUG(("Found"));
1857 entry = id_cache->context;
1858 silc_client_ref_server(client, conn, entry);
1860 silc_mutex_unlock(conn->internal->lock);
1865 /*********************** Server Resolving from Server ***********************/
1867 /* Resolving context */
1870 SilcGetServerCallback completion;
1872 } *SilcClientGetServerInternal;
1874 /* Resolving command callback */
1876 static SilcBool silc_client_get_server_cb(SilcClient client,
1877 SilcClientConnection conn,
1878 SilcCommand command,
1884 SilcClientGetServerInternal i = context;
1885 SilcServerEntry server;
1887 if (error != SILC_STATUS_OK) {
1888 SILC_LOG_DEBUG(("Resolving failed: %s", silc_get_status_message(error)));
1890 i->completion(client, conn, error, NULL, i->context);
1894 /* Add the returned servers to list */
1895 if (i->completion) {
1896 server = va_arg(ap, SilcServerEntry);
1897 silc_client_ref_server(client, conn, server);
1898 silc_dlist_add(i->servers, server);
1899 server->internal.resolve_cmd_ident = 0;
1902 if (status == SILC_STATUS_OK || status == SILC_STATUS_LIST_END) {
1903 /* Deliver the servers to the caller */
1904 if (i->completion) {
1905 SILC_LOG_DEBUG(("Resolved %d servers", silc_dlist_count(i->servers)));
1906 silc_dlist_start(i->servers);
1907 i->completion(client, conn, SILC_STATUS_OK, i->servers, i->context);
1915 silc_client_list_free_servers(client, conn, i->servers);
1920 /* Resolve server by server ID */
1923 silc_client_get_server_by_id_resolve(SilcClient client,
1924 SilcClientConnection conn,
1925 SilcServerID *server_id,
1926 SilcGetServerCallback completion,
1929 SilcClientGetServerInternal i;
1930 SilcServerEntry server;
1932 SilcUInt16 cmd_ident;
1934 if (!client || !conn || !server_id || !completion)
1937 SILC_LOG_DEBUG(("Resolve server by id %s",
1938 silc_id_render(server_id, SILC_ID_SERVER)));
1940 i = silc_calloc(1, sizeof(*i));
1943 i->completion = completion;
1944 i->context = context;
1945 i->servers = silc_dlist_init();
1951 /* Attach to resolving, if on going */
1952 server = silc_client_get_server_by_id(client, conn, server_id);
1953 if (server && server->internal.resolve_cmd_ident) {
1954 SILC_LOG_DEBUG(("Attach to existing resolving"));
1955 silc_client_unref_server(client, conn, server);
1956 silc_client_command_pending(conn, SILC_COMMAND_NONE,
1957 server->internal.resolve_cmd_ident,
1958 silc_client_get_server_cb, i);
1959 return server->internal.resolve_cmd_ident;
1962 /* Send the command */
1963 idp = silc_id_payload_encode(server_id, SILC_ID_SERVER);
1964 cmd_ident = silc_client_command_send(client, conn, SILC_COMMAND_IDENTIFY,
1965 silc_client_get_server_cb, i, 1,
1966 5, silc_buffer_datalen(idp));
1967 silc_buffer_free(idp);
1968 if (!cmd_ident && completion)
1969 completion(client, conn, SILC_STATUS_ERR_RESOURCE_LIMIT, NULL, context);
1971 if (server && cmd_ident)
1972 server->internal.resolve_cmd_ident = cmd_ident;
1974 silc_client_unref_server(client, conn, server);
1979 /************************** Server Entry Routines ***************************/
1981 /* Add new server entry */
1983 SilcServerEntry silc_client_add_server(SilcClient client,
1984 SilcClientConnection conn,
1985 const char *server_name,
1986 const char *server_info,
1987 SilcServerID *server_id)
1989 SilcServerEntry server_entry;
1990 char *server_namec = NULL;
1995 SILC_LOG_DEBUG(("Adding new server %s", server_name));
1997 server_entry = silc_calloc(1, sizeof(*server_entry));
2001 silc_rwlock_alloc(&server_entry->internal.lock);
2002 silc_atomic_init8(&server_entry->internal.refcnt, 0);
2003 server_entry->id = *server_id;
2005 server_entry->server_name = strdup(server_name);
2007 server_entry->server_info = strdup(server_info);
2009 /* Normalize server name */
2011 server_namec = silc_identifier_check(server_name, strlen(server_name),
2012 SILC_STRING_UTF8, 256, NULL);
2013 if (!server_namec) {
2014 silc_free(server_entry->server_name);
2015 silc_free(server_entry->server_info);
2016 silc_free(server_entry);
2021 silc_mutex_lock(conn->internal->lock);
2023 /* Add server to cache */
2024 if (!silc_idcache_add(conn->internal->server_cache, server_namec,
2025 &server_entry->id, server_entry)) {
2026 silc_free(server_namec);
2027 silc_free(server_entry->server_name);
2028 silc_free(server_entry->server_info);
2029 silc_free(server_entry);
2030 silc_mutex_unlock(conn->internal->lock);
2034 silc_mutex_unlock(conn->internal->lock);
2035 silc_client_ref_server(client, conn, server_entry);
2037 SILC_LOG_DEBUG(("Added %p", server_entry));
2039 return server_entry;
2042 /* Removes server from the cache by the server entry. */
2044 SilcBool silc_client_del_server(SilcClient client, SilcClientConnection conn,
2045 SilcServerEntry server)
2047 SilcIDCacheEntry id_cache;
2048 SilcBool ret = TRUE;
2054 if (silc_atomic_sub_int8(&server->internal.refcnt, 1) > 0)
2057 SILC_LOG_DEBUG(("Deleting server %p", server));
2059 silc_mutex_lock(conn->internal->lock);
2060 if (silc_idcache_find_by_context(conn->internal->server_cache, server,
2062 namec = id_cache->name;
2063 ret = silc_idcache_del_by_context(conn->internal->server_cache,
2067 silc_mutex_unlock(conn->internal->lock);
2069 silc_free(server->server_name);
2070 silc_free(server->server_info);
2071 if (server->public_key)
2072 silc_pkcs_public_key_free(server->public_key);
2073 silc_atomic_uninit8(&server->internal.refcnt);
2074 silc_rwlock_free(server->internal.lock);
2080 /* Updates the `server_entry' with the new information sent as argument. */
2082 void silc_client_update_server(SilcClient client,
2083 SilcClientConnection conn,
2084 SilcServerEntry server_entry,
2085 const char *server_name,
2086 const char *server_info)
2088 char *server_namec = NULL;
2090 SILC_LOG_DEBUG(("Updating server %p", server_entry));
2093 (!server_entry->server_name ||
2094 !silc_utf8_strcasecmp(server_entry->server_name, server_name))) {
2096 server_namec = silc_identifier_check(server_name, strlen(server_name),
2097 SILC_STRING_UTF8, 256, NULL);
2101 silc_free(server_entry->server_name);
2102 server_entry->server_name = strdup(server_name);
2103 if (!server_entry->server_name)
2106 /* Update cache entry */
2107 silc_mutex_lock(conn->internal->lock);
2108 silc_idcache_update_by_context(conn->internal->server_cache, server_entry,
2109 NULL, server_namec, TRUE);
2110 silc_mutex_unlock(conn->internal->lock);
2114 (!server_entry->server_info ||
2115 !silc_utf8_strcasecmp(server_entry->server_info, server_info))) {
2116 silc_free(server_entry->server_info);
2117 server_entry->server_info = strdup(server_info);
2123 void silc_client_lock_server(SilcServerEntry server_entry)
2125 silc_rwlock_rdlock(server_entry->internal.lock);
2130 void silc_client_unlock_server(SilcServerEntry server_entry)
2132 silc_rwlock_unlock(server_entry->internal.lock);
2135 /* Take reference of server entry */
2137 SilcServerEntry silc_client_ref_server(SilcClient client,
2138 SilcClientConnection conn,
2139 SilcServerEntry server_entry)
2141 silc_atomic_add_int8(&server_entry->internal.refcnt, 1);
2142 SILC_LOG_DEBUG(("Server %p refcnt %d->%d", server_entry,
2143 silc_atomic_get_int8(&server_entry->internal.refcnt) - 1,
2144 silc_atomic_get_int8(&server_entry->internal.refcnt)));
2145 return server_entry;
2148 /* Release reference of server entry */
2150 void silc_client_unref_server(SilcClient client, SilcClientConnection conn,
2151 SilcServerEntry server_entry)
2154 SILC_LOG_DEBUG(("Server %p refcnt %d->%d", server_entry,
2155 silc_atomic_get_int8(&server_entry->internal.refcnt),
2156 silc_atomic_get_int8(&server_entry->internal.refcnt)
2158 silc_client_del_server(client, conn, server_entry);
2162 /* Free server entry list */
2164 void silc_client_list_free_servers(SilcClient client,
2165 SilcClientConnection conn,
2166 SilcDList server_list)
2168 SilcServerEntry server_entry;
2171 silc_dlist_start(server_list);
2172 while ((server_entry = silc_dlist_get(server_list)))
2173 silc_client_unref_server(client, conn, server_entry);
2175 silc_dlist_uninit(server_list);