5 Author: Pekka Riikonen <priikone@silcnet.org>
7 Copyright (C) 2001 - 2006 Pekka Riikonen
9 This program is free software; you can redistribute it and/or modify
10 it under the terms of the GNU General Public License as published by
11 the Free Software Foundation; version 2 of the License.
13 This program is distributed in the hope that it will be useful,
14 but WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 GNU General Public License for more details.
22 #include "silcclient.h"
23 #include "client_internal.h"
25 /************************ Client Searching Locally **************************/
27 /* Finds entry for client by the client's ID. Returns the entry or NULL
28 if the entry was not found. */
30 SilcClientEntry silc_client_get_client_by_id(SilcClient client,
31 SilcClientConnection conn,
32 SilcClientID *client_id)
34 SilcIDCacheEntry id_cache;
35 SilcClientEntry client_entry;
37 if (!client || !conn || !client_id)
40 SILC_LOG_DEBUG(("Finding client by ID (%s)",
41 silc_id_render(client_id, SILC_ID_CLIENT)));
43 silc_mutex_lock(conn->internal->lock);
45 /* Find ID from cache */
46 if (!silc_idcache_find_by_id_one(conn->internal->client_cache, client_id,
48 silc_mutex_unlock(conn->internal->lock);
52 client_entry = id_cache->context;
55 silc_client_ref_client(client, conn, client_entry);
56 silc_mutex_unlock(conn->internal->lock);
58 SILC_LOG_DEBUG(("Found"));
63 /* Finds clients by nickname from local cache. */
65 SilcDList silc_client_get_clients_local(SilcClient client,
66 SilcClientConnection conn,
70 SilcIDCacheEntry id_cache;
73 SilcClientEntry entry;
76 if (!client || !conn || !nickname)
79 SILC_LOG_DEBUG(("Find clients by nickname %s", nickname));
81 /* Normalize nickname for search */
82 nicknamec = silc_identifier_check(nickname, strlen(nickname),
83 SILC_STRING_UTF8, 128, NULL);
87 clients = silc_dlist_init();
93 silc_mutex_lock(conn->internal->lock);
96 silc_list_init(list, struct SilcIDCacheEntryStruct, next);
97 if (!silc_idcache_find_by_name(conn->internal->client_cache, nicknamec,
99 silc_mutex_unlock(conn->internal->lock);
100 silc_free(nicknamec);
101 silc_dlist_uninit(clients);
106 /* Take all without any further checking */
107 silc_list_start(list);
108 while ((id_cache = silc_list_get(list))) {
109 silc_client_ref_client(client, conn, id_cache->context);
110 silc_dlist_add(clients, id_cache->context);
113 /* Check multiple cache entries for exact match */
114 silc_list_start(list);
115 while ((id_cache = silc_list_get(list))) {
116 entry = id_cache->context;
117 if (silc_utf8_strcasecmp(entry->nickname, format)) {
118 silc_client_ref_client(client, conn, entry);
119 silc_dlist_add(clients, entry);
124 silc_mutex_unlock(conn->internal->lock);
126 silc_dlist_start(clients);
128 silc_free(nicknamec);
132 /********************** Client Resolving from Server ************************/
134 /* Resolving context */
137 SilcGetClientCallback completion;
139 } *SilcClientGetClientInternal;
141 /* Resolving command callback */
143 static SilcBool silc_client_get_clients_cb(SilcClient client,
144 SilcClientConnection conn,
151 SilcClientGetClientInternal i = context;
152 SilcClientEntry client_entry;
154 if (error != SILC_STATUS_OK) {
155 SILC_LOG_DEBUG(("Resolving failed: %s", silc_get_status_message(error)));
157 i->completion(client, conn, error, NULL, i->context);
161 /* Add the returned client to list */
163 client_entry = va_arg(ap, SilcClientEntry);
164 silc_client_ref_client(client, conn, client_entry);
165 silc_dlist_add(i->clients, client_entry);
166 client_entry->internal.resolve_cmd_ident = 0;
169 if (status == SILC_STATUS_OK || status == SILC_STATUS_LIST_END) {
170 /* Deliver the clients to the caller */
172 SILC_LOG_DEBUG(("Resolved %d clients", silc_dlist_count(i->clients)));
173 silc_dlist_start(i->clients);
174 i->completion(client, conn, SILC_STATUS_OK, i->clients, i->context);
182 silc_client_list_free(client, conn, i->clients);
187 /* Resolves client information from server by the client ID. */
190 silc_client_get_client_by_id_resolve(SilcClient client,
191 SilcClientConnection conn,
192 SilcClientID *client_id,
193 SilcBuffer attributes,
194 SilcGetClientCallback completion,
197 SilcClientGetClientInternal i;
198 SilcClientEntry client_entry;
200 SilcUInt16 cmd_ident;
202 if (!client || !conn | !client_id)
205 SILC_LOG_DEBUG(("Resolve client by ID (%s)",
206 silc_id_render(client_id, SILC_ID_CLIENT)));
208 i = silc_calloc(1, sizeof(*i));
211 i->completion = completion;
212 i->context = context;
213 i->clients = silc_dlist_init();
219 /* Attach to resolving, if on going */
220 client_entry = silc_client_get_client_by_id(client, conn, client_id);
221 if (client_entry && client_entry->internal.resolve_cmd_ident) {
222 SILC_LOG_DEBUG(("Attach to existing resolving"));
223 silc_client_unref_client(client, conn, client_entry);
224 silc_client_command_pending(conn, SILC_COMMAND_NONE,
225 client_entry->internal.resolve_cmd_ident,
226 silc_client_get_clients_cb, i);
227 return client_entry->internal.resolve_cmd_ident;
230 /* Send the command */
231 idp = silc_id_payload_encode(client_id, SILC_ID_CLIENT);
232 cmd_ident = silc_client_command_send(client, conn, SILC_COMMAND_WHOIS,
233 silc_client_get_clients_cb, i,
234 2, 3, silc_buffer_datalen(attributes),
235 4, silc_buffer_datalen(idp));
236 if (!cmd_ident && completion)
237 completion(client, conn, SILC_STATUS_ERR_RESOURCE_LIMIT, NULL, context);
239 if (client_entry && cmd_ident)
240 client_entry->internal.resolve_cmd_ident = cmd_ident;
242 silc_client_unref_client(client, conn, client_entry);
243 silc_buffer_free(idp);
248 /* Finds client entry or entries by the `nickname' and `server'. The
249 completion callback will be called when the client entries has been
250 found. Used internally by the library. */
252 static SilcUInt16 silc_client_get_clients_i(SilcClient client,
253 SilcClientConnection conn,
255 const char *nickname,
257 SilcBuffer attributes,
258 SilcGetClientCallback completion,
261 SilcClientGetClientInternal i;
262 char userhost[768 + 1];
265 SILC_LOG_DEBUG(("Resolve client by %s command",
266 silc_get_command_name(command)));
268 if (!client || !conn)
270 if (!nickname && !attributes)
273 i = silc_calloc(1, sizeof(*i));
276 i->clients = silc_dlist_init();
281 i->completion = completion;
282 i->context = context;
284 memset(userhost, 0, sizeof(userhost));
285 if (nickname && server) {
286 len = strlen(nickname) + strlen(server) + 3;
287 silc_strncat(userhost, len, nickname, strlen(nickname));
288 silc_strncat(userhost, len, "@", 1);
289 silc_strncat(userhost, len, server, strlen(server));
290 } else if (nickname) {
291 silc_strncat(userhost, sizeof(userhost) - 1, nickname, strlen(nickname));
294 /* Send the command */
295 if (command == SILC_COMMAND_IDENTIFY)
296 return silc_client_command_send(client, conn, SILC_COMMAND_IDENTIFY,
297 silc_client_get_clients_cb, i,
298 1, 1, userhost, strlen(userhost));
299 return silc_client_command_send(client, conn, SILC_COMMAND_WHOIS,
300 silc_client_get_clients_cb, i,
301 2, 1, userhost, strlen(userhost),
302 3, silc_buffer_datalen(attributes));
305 /* Get clients from server with IDENTIFY command */
307 SilcUInt16 silc_client_get_clients(SilcClient client,
308 SilcClientConnection conn,
309 const char *nickname,
311 SilcGetClientCallback completion,
314 return silc_client_get_clients_i(client, conn, SILC_COMMAND_IDENTIFY,
315 nickname, server, NULL,
316 completion, context);
319 /* Get clients from server with WHOIS command */
321 SilcUInt16 silc_client_get_clients_whois(SilcClient client,
322 SilcClientConnection conn,
323 const char *nickname,
325 SilcBuffer attributes,
326 SilcGetClientCallback completion,
329 return silc_client_get_clients_i(client, conn, SILC_COMMAND_WHOIS,
330 nickname, server, attributes,
331 completion, context);
334 /* ID list resolving context */
336 SilcGetClientCallback completion;
338 SilcBuffer client_id_list;
339 SilcUInt32 list_count;
340 } *GetClientsByListInternal;
342 static SilcBool silc_client_get_clients_list_cb(SilcClient client,
343 SilcClientConnection conn,
350 GetClientsByListInternal i = context;
351 SilcClientEntry client_entry;
357 /* Process the list after all replies have been received */
358 if (status != SILC_STATUS_OK && !SILC_STATUS_IS_ERROR(status) &&
359 status != SILC_STATUS_LIST_END)
362 SILC_LOG_DEBUG(("Resolved all clients"));
364 clients = silc_dlist_init();
366 status = SILC_STATUS_ERR_RESOURCE_LIMIT;
370 for (c = 0; c < i->list_count; c++) {
372 SILC_GET16_MSB(idp_len, i->client_id_list->data + 2);
374 if (!silc_id_payload_parse_id(i->client_id_list->data, idp_len, &id)) {
375 status = SILC_STATUS_ERR_BAD_CLIENT_ID;
379 /* Get client entry */
380 client_entry = silc_client_get_client_by_id(client, conn, &id.u.client_id);
382 silc_dlist_add(clients, client_entry);
384 if (!silc_buffer_pull(i->client_id_list, idp_len)) {
385 status = SILC_STATUS_ERR_BAD_CLIENT_ID;
390 silc_dlist_start(clients);
391 status = SILC_STATUS_OK;
393 i->completion(client, conn, status, clients, i->context);
396 if (status != SILC_STATUS_OK && i->completion)
397 i->completion(client, conn, status, NULL, i->context);
399 silc_client_list_free(client, conn, clients);
400 silc_buffer_free(i->client_id_list);
406 /* Gets client entries by the list of client ID's `client_id_list'. This
407 always resolves those client ID's it does not know yet from the server
408 so this function might take a while. The `client_id_list' is a list
409 of ID Payloads added one after other. JOIN command reply and USERS
410 command reply for example returns this sort of list. The `completion'
411 will be called after the entries are available. */
413 SilcUInt16 silc_client_get_clients_by_list(SilcClient client,
414 SilcClientConnection conn,
415 SilcUInt32 list_count,
416 SilcBuffer client_id_list,
417 SilcGetClientCallback completion,
420 GetClientsByListInternal in;
421 SilcClientEntry entry;
422 unsigned char **res_argv = NULL;
423 SilcUInt32 *res_argv_lens = NULL, *res_argv_types = NULL, res_argc = 0;
424 SilcUInt16 idp_len, cmd_ident;
429 SILC_LOG_DEBUG(("Resolve clients from Client ID list"));
431 if (!client || !conn || !client_id_list)
434 in = silc_calloc(1, sizeof(*in));
437 in->completion = completion;
438 in->context = context;
439 in->list_count = list_count;
440 in->client_id_list = silc_buffer_copy(client_id_list);
441 if (!in->client_id_list)
444 for (i = 0; i < list_count; i++) {
446 SILC_GET16_MSB(idp_len, client_id_list->data + 2);
448 if (!silc_id_payload_parse_id(client_id_list->data, idp_len, &id))
451 /* Check if we have this client cached already. If we don't have the
452 entry or it has incomplete info, then resolve it from the server. */
453 entry = silc_client_get_client_by_id(client, conn, &id.u.client_id);
454 if (!entry || !entry->nickname[0] || !entry->username[0] ||
457 res_argv = silc_calloc(list_count, sizeof(*res_argv));
458 res_argv_lens = silc_calloc(list_count, sizeof(*res_argv_lens));
459 res_argv_types = silc_calloc(list_count, sizeof(*res_argv_types));
460 if (!res_argv || !res_argv_lens || !res_argv_types) {
461 silc_client_unref_client(client, conn, entry);
466 res_argv[res_argc] = client_id_list->data;
467 res_argv_lens[res_argc] = idp_len;
468 res_argv_types[res_argc] = res_argc + 4;
471 silc_client_unref_client(client, conn, entry);
473 if (!silc_buffer_pull(client_id_list, idp_len))
476 silc_buffer_start(client_id_list);
478 /* Query the unknown client information from server */
480 cmd_ident = silc_client_command_send_argv(client,
481 conn, SILC_COMMAND_WHOIS,
482 silc_client_get_clients_list_cb,
483 in, res_argc, res_argv,
487 silc_free(res_argv_lens);
488 silc_free(res_argv_types);
492 /* We have the clients in cache, get them and call the completion */
493 silc_client_get_clients_list_cb(client, conn, SILC_COMMAND_WHOIS,
494 SILC_STATUS_OK, SILC_STATUS_OK, in, tmp);
498 silc_buffer_free(in->client_id_list);
501 silc_free(res_argv_lens);
502 silc_free(res_argv_types);
509 SilcClientConnection conn;
510 SilcChannelID channel_id;
511 SilcGetClientCallback completion;
514 } *GetClientsByChannelInternal;
516 SILC_CLIENT_CMD_FUNC(get_clients_by_channel_cb)
518 GetClientsByChannelInternal i = context;
519 SilcClientEntry *clients = NULL;
520 SilcUInt32 clients_count = 0;
521 SilcBool found = FALSE;
522 SilcChannelEntry channel;
523 SilcHashTableList htl;
532 channel = silc_client_get_channel_by_id(i->client, i->conn, &i->channel_id);
533 if (channel && !silc_hash_table_count(channel->user_list)) {
534 clients = silc_calloc(silc_hash_table_count(channel->user_list),
536 silc_hash_table_list(channel->user_list, &htl);
537 while (silc_hash_table_get(&htl, NULL, (void *)&chu))
538 clients[clients_count++] = chu->client;
539 silc_hash_table_list_reset(&htl);
544 i->completion(i->client, i->conn, clients, clients_count, i->context);
547 i->completion(i->client, i->conn, NULL, 0, i->context);
553 /* Gets client entries by the channel entry indicated by `channel'. Thus,
554 it resolves the clients currently on that channel. */
556 void silc_client_get_clients_by_channel(SilcClient client,
557 SilcClientConnection conn,
558 SilcChannelEntry channel,
559 SilcGetClientCallback completion,
562 GetClientsByChannelInternal in;
563 SilcHashTableList htl;
565 SilcClientEntry entry;
566 unsigned char **res_argv = NULL;
567 SilcUInt32 *res_argv_lens = NULL, *res_argv_types = NULL, res_argc = 0;
569 SilcBool wait_res = FALSE;
571 assert(client && conn && channel);
573 SILC_LOG_DEBUG(("Start"));
575 in = silc_calloc(1, sizeof(*in));
578 in->channel_id = *channel->id;
579 in->completion = completion;
580 in->context = context;
582 /* If user list does not exist, send USERS command. */
583 if (!channel->user_list || !silc_hash_table_count(channel->user_list)) {
584 SILC_LOG_DEBUG(("Sending USERS"));
585 silc_client_command_register(client, SILC_COMMAND_USERS, NULL, NULL,
586 silc_client_command_reply_users_i, 0,
588 silc_client_command_send(client, conn, SILC_COMMAND_USERS,
589 conn->cmd_ident, 1, 2, channel->channel_name,
590 strlen(channel->channel_name));
591 silc_client_command_pending(conn, SILC_COMMAND_USERS, conn->cmd_ident,
592 silc_client_command_get_clients_by_channel_cb,
597 silc_hash_table_list(channel->user_list, &htl);
598 while (silc_hash_table_get(&htl, NULL, (void *)&chu)) {
601 /* If the entry has incomplete info, then resolve it from the server. */
602 if (!entry->nickname[0] || !entry->realname) {
603 if (entry->status & SILC_CLIENT_STATUS_RESOLVING) {
604 /* Attach to this resolving and wait until it finishes */
605 silc_client_command_pending(
606 conn, SILC_COMMAND_NONE,
607 entry->resolve_cmd_ident,
608 silc_client_command_get_clients_by_channel_cb,
614 entry->status |= SILC_CLIENT_STATUS_RESOLVING;
615 entry->resolve_cmd_ident = conn->cmd_ident + 1;
617 idp = silc_id_payload_encode(entry->id, SILC_ID_CLIENT);
619 /* No we don't have it, query it from the server. Assemble argument
620 table that will be sent for the WHOIS command later. */
621 res_argv = silc_realloc(res_argv, sizeof(*res_argv) *
623 res_argv_lens = silc_realloc(res_argv_lens, sizeof(*res_argv_lens) *
625 res_argv_types = silc_realloc(res_argv_types, sizeof(*res_argv_types) *
627 res_argv[res_argc] = silc_memdup(idp->data, idp->len);
628 res_argv_lens[res_argc] = idp->len;
629 res_argv_types[res_argc] = res_argc + 4;
632 silc_buffer_free(idp);
635 silc_hash_table_list_reset(&htl);
637 /* Query the client information from server if the list included clients
638 that we don't know about. */
642 /* Send the WHOIS command to server */
643 res_cmd = silc_command_payload_encode(SILC_COMMAND_WHOIS,
644 res_argc, res_argv, res_argv_lens,
645 res_argv_types, ++conn->cmd_ident);
646 silc_client_packet_send(client, conn->sock, SILC_PACKET_COMMAND,
647 NULL, 0, NULL, NULL, res_cmd->data, res_cmd->len,
650 /* Register our own command reply for this command */
651 silc_client_command_register(client, SILC_COMMAND_WHOIS, NULL, NULL,
652 silc_client_command_reply_whois_i, 0,
655 /* Process the applications request after reply has been received */
656 silc_client_command_pending(
657 conn, SILC_COMMAND_WHOIS, conn->cmd_ident,
658 silc_client_command_get_clients_by_channel_cb,
662 silc_buffer_free(res_cmd);
664 silc_free(res_argv_lens);
665 silc_free(res_argv_types);
672 /* We have the clients in cache, get them and call the completion */
673 silc_client_command_get_clients_by_channel_cb((void *)in, NULL);
678 /************************** Client Entry Routines ***************************/
680 /* Creates new client entry and adds it to the ID cache. Returns pointer
683 SilcClientEntry silc_client_add_client(SilcClient client,
684 SilcClientConnection conn,
685 char *nickname, char *username,
686 char *userinfo, SilcClientID *id,
689 SilcClientEntry client_entry;
692 SILC_LOG_DEBUG(("Adding new client entry"));
694 /* Save the client infos */
695 client_entry = silc_calloc(1, sizeof(*client_entry));
699 silc_atomic_init8(&client_entry->internal.refcnt, 0);
700 client_entry->id = *id;
701 client_entry->internal.valid = TRUE;
702 client_entry->mode = mode;
703 client_entry->realname = userinfo ? strdup(userinfo) : NULL;
704 silc_parse_userfqdn(nickname, client_entry->nickname,
705 sizeof(client_entry->nickname),
706 client_entry->server,
707 sizeof(client_entry->server));
708 silc_parse_userfqdn(username, client_entry->username,
709 sizeof(client_entry->username),
710 client_entry->hostname,
711 sizeof(client_entry->hostname));
712 client_entry->channels = silc_hash_table_alloc(1, silc_hash_ptr, NULL, NULL,
713 NULL, NULL, NULL, TRUE);
714 if (!client_entry->channels) {
715 silc_free(client_entry->realname);
716 silc_free(client_entry);
720 /* Normalize nickname */
721 if (client_entry->nickname[0]) {
722 nick = silc_identifier_check(client_entry->nickname,
723 strlen(client_entry->nickname),
724 SILC_STRING_UTF8, 128, NULL);
726 silc_free(client_entry->realname);
727 silc_hash_table_free(client_entry->channels);
728 silc_free(client_entry);
733 /* Format the nickname */
734 silc_client_nickname_format(client, conn, client_entry, FALSE);
736 silc_mutex_lock(conn->internal->lock);
738 /* Add client to cache, the normalized nickname is saved to cache */
739 if (!silc_idcache_add(conn->internal->client_cache, nick,
740 &client_entry->id, client_entry)) {
742 silc_free(client_entry->realname);
743 silc_hash_table_free(client_entry->channels);
744 silc_free(client_entry);
745 silc_mutex_unlock(conn->internal->lock);
749 client_entry->nickname_normalized = nick;
751 silc_mutex_unlock(conn->internal->lock);
752 silc_client_ref_client(client, conn, client_entry);
754 SILC_LOG_DEBUG(("Added %p", client_entry));
759 /* Updates the `client_entry' with the new information sent as argument. */
761 void silc_client_update_client(SilcClient client,
762 SilcClientConnection conn,
763 SilcClientEntry client_entry,
764 const char *nickname,
765 const char *username,
766 const char *userinfo,
771 SILC_LOG_DEBUG(("Update client entry"));
773 if (!client_entry->realname && userinfo)
774 client_entry->realname = strdup(userinfo);
775 if ((!client_entry->username[0] || !client_entry->hostname[0]) && username)
776 silc_parse_userfqdn(username, client_entry->username,
777 sizeof(client_entry->username),
778 client_entry->hostname,
779 sizeof(client_entry->username));
780 if (!client_entry->nickname[0] && nickname) {
781 silc_parse_userfqdn(nickname, client_entry->nickname,
782 sizeof(client_entry->nickname),
783 client_entry->server,
784 sizeof(client_entry->server));
786 /* Normalize nickname */
787 nick = silc_identifier_check(client_entry->nickname,
788 strlen(client_entry->nickname),
789 SILC_STRING_UTF8, 128, NULL);
793 /* Format nickname */
794 silc_client_nickname_format(client, conn, client_entry,
795 client_entry == conn->local_entry);
797 /* Update cache entry */
798 silc_mutex_lock(conn->internal->lock);
799 silc_idcache_update_by_context(conn->internal->client_cache,
800 client_entry, NULL, nick, TRUE);
801 silc_mutex_unlock(conn->internal->lock);
802 client_entry->nickname_normalized = nick;
804 client_entry->mode = mode;
807 /* Change a client's nickname */
809 SilcBool silc_client_change_nickname(SilcClient client,
810 SilcClientConnection conn,
811 SilcClientEntry client_entry,
812 const char *new_nick,
813 SilcClientID *new_id,
814 const unsigned char *idp,
819 SILC_LOG_DEBUG(("Change nickname %s to %s", client_entry->nickname,
822 /* Normalize nickname */
823 tmp = silc_identifier_check(new_nick, strlen(new_nick),
824 SILC_STRING_UTF8, 128, NULL);
828 /* Update the client entry */
829 silc_mutex_lock(conn->internal->lock);
830 if (!silc_idcache_update_by_context(conn->internal->client_cache,
831 client_entry, new_id, tmp, TRUE)) {
833 silc_mutex_unlock(conn->internal->lock);
836 silc_mutex_unlock(conn->internal->lock);
838 memset(client_entry->nickname, 0, sizeof(client_entry->nickname));
839 memcpy(client_entry->nickname, new_nick, strlen(new_nick));
840 client_entry->nickname_normalized = tmp;
841 silc_client_nickname_format(client, conn, client_entry,
842 client_entry == conn->local_entry);
844 /* For my client entry, update ID and set new ID to packet stream */
845 if (client_entry == conn->local_entry) {
846 if (idp && idp_len) {
847 silc_buffer_enlarge(conn->internal->local_idp, idp_len);
848 silc_buffer_put(conn->internal->local_idp, idp, idp_len);
851 silc_packet_set_ids(conn->stream, SILC_ID_CLIENT, conn->local_id,
858 /* Deletes the client entry and frees all memory. */
860 void silc_client_del_client_entry(SilcClient client,
861 SilcClientConnection conn,
862 SilcClientEntry client_entry)
864 silc_free(client_entry->realname);
865 silc_free(client_entry->nickname_normalized);
866 silc_free(client_entry->internal.key);
867 if (client_entry->public_key)
868 silc_pkcs_public_key_free(client_entry->public_key);
869 silc_hash_table_free(client_entry->channels);
870 if (client_entry->internal.send_key)
871 silc_cipher_free(client_entry->internal.send_key);
872 if (client_entry->internal.receive_key)
873 silc_cipher_free(client_entry->internal.receive_key);
874 if (client_entry->internal.hmac_send)
875 silc_hmac_free(client_entry->internal.hmac_send);
876 if (client_entry->internal.hmac_receive)
877 silc_hmac_free(client_entry->internal.hmac_receive);
879 silc_client_ftp_session_free_client(conn, client_entry);
880 if (client_entry->internal->ke)
881 silc_client_abort_key_agreement(client, conn, client_entry);
883 silc_atomic_uninit8(&client_entry->internal.refcnt);
884 silc_free(client_entry);
887 /* Removes client from the cache by the client entry. */
889 SilcBool silc_client_del_client(SilcClient client, SilcClientConnection conn,
890 SilcClientEntry client_entry)
897 if (silc_atomic_sub_int8(&client_entry->internal.refcnt, 1) > 0)
900 SILC_LOG_DEBUG(("Deleting client %p", client_entry));
902 silc_mutex_lock(conn->internal->lock);
903 ret = silc_idcache_del_by_context(conn->internal->client_cache,
905 silc_mutex_unlock(conn->internal->lock);
908 /* Remove from channels */
909 silc_client_remove_from_channels(client, conn, client_entry);
911 /* Free the client entry data */
912 silc_client_del_client_entry(client, conn, client_entry);
918 /* Take reference of client entry */
920 void silc_client_ref_client(SilcClient client, SilcClientConnection conn,
921 SilcClientEntry client_entry)
923 silc_atomic_add_int8(&client_entry->internal.refcnt, 1);
924 SILC_LOG_DEBUG(("Client %p refcnt %d->%d", client_entry,
925 silc_atomic_get_int8(&client_entry->internal.refcnt) - 1,
926 silc_atomic_get_int8(&client_entry->internal.refcnt)));
929 /* Release reference of client entry */
931 void silc_client_unref_client(SilcClient client, SilcClientConnection conn,
932 SilcClientEntry client_entry)
935 SILC_LOG_DEBUG(("Client %p refcnt %d->%d", client_entry,
936 silc_atomic_get_int8(&client_entry->internal.refcnt),
937 silc_atomic_get_int8(&client_entry->internal.refcnt) - 1));
938 silc_client_del_client(client, conn, client_entry);
942 /* Free client entry list */
944 void silc_client_list_free(SilcClient client, SilcClientConnection conn,
945 SilcDList client_list)
947 SilcClientEntry client_entry;
950 silc_dlist_start(client_list);
951 while ((client_entry = silc_dlist_get(client_list)))
952 silc_client_unref_client(client, conn, client_entry);
954 silc_dlist_uninit(client_list);
958 /* Formats the nickname of the client specified by the `client_entry'.
959 If the format is specified by the application this will format the
960 nickname and replace the old nickname in the client entry. If the
961 format string is not specified then this function has no effect.
962 Returns the client entry that was formatted. */
964 SilcClientEntry silc_client_nickname_format(SilcClient client,
965 SilcClientConnection conn,
966 SilcClientEntry client_entry,
970 char newnick[128 + 1];
974 SilcClientEntry entry, unformatted = NULL;
976 SILC_LOG_DEBUG(("Format nickname"));
978 if (!client->internal->params->nickname_format[0])
980 if (!client_entry->nickname[0])
983 /* Get all clients with same nickname. Do not perform the formatting
984 if there aren't any clients with same nickname unless the application
985 is forcing us to do so. */
986 clients = silc_client_get_clients_local(client, conn,
987 client_entry->nickname, NULL);
990 if (silc_dlist_count(clients) == 1 &&
991 !client->internal->params->nickname_force_format) {
992 silc_client_list_free(client, conn, clients);
998 while ((entry = silc_dlist_get(clients))) {
999 if (entry->internal.valid && entry != client_entry)
1001 if (entry->internal.valid && entry != client_entry &&
1002 silc_utf8_strcasecmp(entry->nickname, client_entry->nickname)) {
1004 unformatted = entry;
1008 if (!len || freebase) {
1009 silc_client_list_free(client, conn, clients);
1010 return client_entry;
1013 /* If priority formatting, this client always gets unformatted nickname. */
1014 if (unformatted && priority)
1015 client_entry = unformatted;
1017 memset(newnick, 0, sizeof(newnick));
1018 cp = client->internal->params->nickname_format;
1028 if (!client_entry->nickname[0])
1030 len = strlen(client_entry->nickname);
1031 memcpy(&newnick[off], client_entry->nickname, len);
1035 /* Stripped hostname */
1036 if (!client_entry->hostname[0])
1038 len = strcspn(client_entry->hostname, ".");
1039 i = strcspn(client_entry->hostname, "-");
1042 memcpy(&newnick[off], client_entry->hostname, len);
1047 if (!client_entry->hostname[0])
1049 len = strlen(client_entry->hostname);
1050 memcpy(&newnick[off], client_entry->hostname, len);
1054 /* Stripped server name */
1055 if (!client_entry->server)
1057 len = strcspn(client_entry->server, ".");
1058 memcpy(&newnick[off], client_entry->server, len);
1062 /* Full server name */
1063 if (!client_entry->server)
1065 len = strlen(client_entry->server);
1066 memcpy(&newnick[off], client_entry->server, len);
1070 /* Ascending number */
1075 if (silc_dlist_count(clients) == 1)
1078 silc_dlist_start(clients);
1079 while ((entry = silc_dlist_get(clients))) {
1080 if (!silc_utf8_strncasecmp(entry->nickname, newnick, off))
1082 if (strlen(entry->nickname) <= off)
1084 num = atoi(&entry->nickname[off]);
1089 memset(tmp, 0, sizeof(tmp));
1090 silc_snprintf(tmp, sizeof(tmp) - 1, "%d", ++max);
1092 memcpy(&newnick[off], tmp, len);
1097 /* Some other character in the string */
1098 memcpy(&newnick[off], cp, 1);
1107 memcpy(client_entry->nickname, newnick, strlen(newnick));
1108 silc_client_list_free(client, conn, clients);
1110 return client_entry;
1113 /* Parses nickname according to nickname format string */
1115 SilcBool silc_client_nickname_parse(SilcClient client,
1116 SilcClientConnection conn,
1120 char *cp, s = 0, e = 0, *nick;
1124 if (!client->internal->params->nickname_format[0])
1127 if (!nickname || !nickname[0])
1130 cp = client->internal->params->nickname_format;
1150 /* Get separator character */
1163 /* Parse the nickname */
1167 if (strchr(nickname, s))
1168 nick = strchr(nickname, s) + 1;
1170 if (strchr(nick, e))
1171 len = strchr(nick, e) - nick;
1175 *ret_nick = silc_memdup(nick, len);
1179 SILC_LOG_DEBUG(("Parsed nickname: %s", *ret_nick));
1184 /************************ Channel Searching Locally *************************/
1186 /* Finds entry for channel by the channel name. Returns the entry or NULL
1187 if the entry was not found. It is found only if the client is joined
1190 SilcChannelEntry silc_client_get_channel(SilcClient client,
1191 SilcClientConnection conn,
1194 SilcIDCacheEntry id_cache;
1195 SilcChannelEntry entry;
1197 if (!client || !conn || !channel)
1200 SILC_LOG_DEBUG(("Find channel %s", channel));
1202 /* Normalize name for search */
1203 channel = silc_channel_name_check(channel, strlen(channel), SILC_STRING_UTF8,
1208 silc_mutex_lock(conn->internal->lock);
1210 if (!silc_idcache_find_by_name_one(conn->internal->channel_cache, channel,
1212 silc_mutex_unlock(conn->internal->lock);
1217 SILC_LOG_DEBUG(("Found"));
1219 entry = id_cache->context;
1222 silc_client_ref_channel(client, conn, entry);
1223 silc_mutex_unlock(conn->internal->lock);
1230 /* Finds entry for channel by the channel ID. Returns the entry or NULL
1231 if the entry was not found. It is found only if the client is joined
1234 SilcChannelEntry silc_client_get_channel_by_id(SilcClient client,
1235 SilcClientConnection conn,
1236 SilcChannelID *channel_id)
1238 SilcIDCacheEntry id_cache;
1239 SilcChannelEntry entry;
1241 if (!client || !conn || !channel_id)
1244 SILC_LOG_DEBUG(("Find channel by id %s",
1245 silc_id_render(channel_id, SILC_ID_CHANNEL)));
1247 silc_mutex_lock(conn->internal->lock);
1249 if (!silc_idcache_find_by_id_one(conn->internal->channel_cache, channel_id,
1251 silc_mutex_unlock(conn->internal->lock);
1255 SILC_LOG_DEBUG(("Found"));
1257 entry = id_cache->context;
1260 silc_client_ref_channel(client, conn, entry);
1261 silc_mutex_unlock(conn->internal->lock);
1266 /********************** Channel Resolving from Server ***********************/
1268 /* Channel resolving context */
1271 SilcGetChannelCallback completion;
1273 } *SilcClientGetChannelInternal;
1275 /* Resolving command callback */
1277 static SilcBool silc_client_get_channel_cb(SilcClient client,
1278 SilcClientConnection conn,
1279 SilcCommand command,
1285 SilcClientGetChannelInternal i = context;
1286 SilcChannelEntry entry;
1288 if (error != SILC_STATUS_OK) {
1289 SILC_LOG_DEBUG(("Resolving failed: %s", silc_get_status_message(error)));
1291 i->completion(client, conn, error, NULL, i->context);
1295 /* Add the returned channel to list */
1296 if (i->completion) {
1297 entry = va_arg(ap, SilcChannelEntry);
1298 silc_client_ref_channel(client, conn, entry);
1299 silc_dlist_add(i->channels, entry);
1302 if (status == SILC_STATUS_OK || status == SILC_STATUS_LIST_END) {
1303 /* Deliver the channels to the caller */
1304 if (i->completion) {
1305 SILC_LOG_DEBUG(("Resolved %d channels", silc_dlist_count(i->channels)));
1306 silc_dlist_start(i->channels);
1307 i->completion(client, conn, SILC_STATUS_OK, i->channels, i->context);
1315 silc_client_list_free_channels(client, conn, i->channels);
1320 /* Resolves channel entry from the server by the channel name. */
1322 void silc_client_get_channel_resolve(SilcClient client,
1323 SilcClientConnection conn,
1325 SilcGetChannelCallback completion,
1328 SilcClientGetChannelInternal i;
1330 if (!client || !conn || !channel_name || !completion)
1333 SILC_LOG_DEBUG(("Resolve channel %s", channel_name));
1335 i = silc_calloc(1, sizeof(*i));
1338 i->completion = completion;
1339 i->context = context;
1340 i->channels = silc_dlist_init();
1346 /* Send the command */
1347 if (!silc_client_command_send(client, conn, SILC_COMMAND_IDENTIFY,
1348 silc_client_get_channel_cb, i, 1,
1349 3, channel_name, strlen(channel_name))) {
1351 completion(client, conn, SILC_STATUS_ERR_RESOURCE_LIMIT, NULL, context);
1355 /* Resolves channel information from the server by the channel ID. */
1358 silc_client_get_channel_by_id_resolve(SilcClient client,
1359 SilcClientConnection conn,
1360 SilcChannelID *channel_id,
1361 SilcGetChannelCallback completion,
1364 SilcClientGetChannelInternal i;
1366 SilcUInt16 cmd_ident;
1368 if (!client || !conn || !channel_id || !completion)
1371 SILC_LOG_DEBUG(("Resolve channel by id %s",
1372 silc_id_render(channel_id, SILC_ID_CHANNEL)));
1374 i = silc_calloc(1, sizeof(*i));
1377 i->completion = completion;
1378 i->context = context;
1379 i->channels = silc_dlist_init();
1385 /* Send the command */
1386 idp = silc_id_payload_encode(channel_id, SILC_ID_CHANNEL);
1387 cmd_ident = silc_client_command_send(client, conn, SILC_COMMAND_IDENTIFY,
1388 silc_client_get_channel_cb, i, 1,
1389 5, silc_buffer_datalen(idp));
1390 silc_buffer_free(idp);
1391 if (!cmd_ident && completion)
1392 completion(client, conn, SILC_STATUS_ERR_RESOURCE_LIMIT, NULL, context);
1397 /************************* Channel Entry Routines ***************************/
1399 /* Add new channel entry to the ID Cache */
1401 SilcChannelEntry silc_client_add_channel(SilcClient client,
1402 SilcClientConnection conn,
1403 const char *channel_name,
1405 SilcChannelID *channel_id)
1407 SilcChannelEntry channel;
1408 char *channel_namec;
1410 SILC_LOG_DEBUG(("Start"));
1412 channel = silc_calloc(1, sizeof(*channel));
1416 silc_atomic_init16(&channel->internal.refcnt, 0);
1417 channel->id = *channel_id;
1418 channel->mode = mode;
1420 channel->channel_name = strdup(channel_name);
1421 if (!channel->channel_name) {
1426 channel->user_list = silc_hash_table_alloc(1, silc_hash_ptr, NULL, NULL,
1427 NULL, NULL, NULL, TRUE);
1428 if (!channel->user_list) {
1429 silc_free(channel->channel_name);
1434 /* Normalize channel name */
1435 channel_namec = silc_channel_name_check(channel_name, strlen(channel_name),
1436 SILC_STRING_UTF8, 256, NULL);
1437 if (!channel_namec) {
1438 silc_free(channel->channel_name);
1439 silc_hash_table_free(channel->user_list);
1444 silc_mutex_lock(conn->internal->lock);
1446 /* Add channel to cache, the normalized channel name is saved to cache */
1447 if (!silc_idcache_add(conn->internal->channel_cache, channel_namec,
1448 &channel->id, channel)) {
1449 silc_free(channel_namec);
1450 silc_free(channel->channel_name);
1451 silc_hash_table_free(channel->user_list);
1453 silc_mutex_unlock(conn->internal->lock);
1457 silc_mutex_unlock(conn->internal->lock);
1458 silc_client_ref_channel(client, conn, channel);
1460 SILC_LOG_DEBUG(("Added %p", channel));
1465 /* Removes channel from the cache by the channel entry. */
1467 SilcBool silc_client_del_channel(SilcClient client, SilcClientConnection conn,
1468 SilcChannelEntry channel)
1477 if (silc_atomic_sub_int16(&channel->internal.refcnt, 1) > 0)
1480 SILC_LOG_DEBUG(("Deleting channel %p", channel));
1482 silc_mutex_lock(conn->internal->lock);
1483 ret = silc_idcache_del_by_context(conn->internal->channel_cache,
1485 silc_mutex_unlock(conn->internal->lock);
1490 silc_client_empty_channel(client, conn, channel);
1491 silc_hash_table_free(channel->user_list);
1492 silc_free(channel->channel_name);
1493 silc_free(channel->topic);
1494 if (channel->founder_key)
1495 silc_pkcs_public_key_free(channel->founder_key);
1496 if (channel->internal.send_key)
1497 silc_cipher_free(channel->internal.send_key);
1498 if (channel->internal.receive_key)
1499 silc_cipher_free(channel->internal.receive_key);
1500 if (channel->internal.hmac)
1501 silc_hmac_free(channel->internal.hmac);
1502 if (channel->internal.old_channel_keys) {
1503 silc_dlist_start(channel->internal.old_channel_keys);
1504 while ((key = silc_dlist_get(channel->internal.old_channel_keys)))
1505 silc_cipher_free(key);
1506 silc_dlist_uninit(channel->internal.old_channel_keys);
1508 if (channel->internal.old_hmacs) {
1509 silc_dlist_start(channel->internal.old_hmacs);
1510 while ((hmac = silc_dlist_get(channel->internal.old_hmacs)))
1511 silc_hmac_free(hmac);
1512 silc_dlist_uninit(channel->internal.old_hmacs);
1514 if (channel->channel_pubkeys)
1515 silc_argument_list_free(channel->channel_pubkeys,
1516 SILC_ARGUMENT_PUBLIC_KEY);
1517 silc_client_del_channel_private_keys(client, conn, channel);
1518 silc_atomic_uninit16(&channel->internal.refcnt);
1519 silc_schedule_task_del_by_context(conn->client->schedule, channel);
1525 /* Replaces the channel ID of the `channel' to `new_id'. Returns FALSE
1526 if the ID could not be changed. */
1528 SilcBool silc_client_replace_channel_id(SilcClient client,
1529 SilcClientConnection conn,
1530 SilcChannelEntry channel,
1531 SilcChannelID *new_id)
1533 SilcBool ret = FALSE;
1538 SILC_LOG_DEBUG(("Old Channel ID id(%s)",
1539 silc_id_render(&channel->id, SILC_ID_CHANNEL)));
1540 SILC_LOG_DEBUG(("New Channel ID id(%s)",
1541 silc_id_render(new_id, SILC_ID_CHANNEL)));
1544 silc_mutex_lock(conn->internal->lock);
1545 silc_idcache_update_by_context(conn->internal->channel_cache, channel,
1546 new_id, NULL, FALSE);
1547 silc_mutex_unlock(conn->internal->lock);
1552 /* Take reference of channel entry */
1554 void silc_client_ref_channel(SilcClient client, SilcClientConnection conn,
1555 SilcChannelEntry channel_entry)
1557 silc_atomic_add_int16(&channel_entry->internal.refcnt, 1);
1558 SILC_LOG_DEBUG(("Channel %p refcnt %d->%d", channel_entry,
1559 silc_atomic_get_int16(&channel_entry->internal.refcnt) - 1,
1560 silc_atomic_get_int16(&channel_entry->internal.refcnt)));
1563 /* Release reference of channel entry */
1565 void silc_client_unref_channel(SilcClient client, SilcClientConnection conn,
1566 SilcChannelEntry channel_entry)
1568 if (channel_entry) {
1569 SILC_LOG_DEBUG(("Channel %p refcnt %d->%d", channel_entry,
1570 silc_atomic_get_int16(&channel_entry->internal.refcnt),
1571 silc_atomic_get_int16(&channel_entry->internal.refcnt)
1573 silc_client_del_channel(client, conn, channel_entry);
1577 /* Free channel entry list */
1579 void silc_client_list_free_channels(SilcClient client,
1580 SilcClientConnection conn,
1581 SilcDList channel_list)
1583 SilcChannelEntry channel_entry;
1586 silc_dlist_start(channel_list);
1587 while ((channel_entry = silc_dlist_get(channel_list)))
1588 silc_client_unref_channel(client, conn, channel_entry);
1590 silc_dlist_uninit(channel_list);
1594 /************************* Server Searching Locally *************************/
1596 /* Finds entry for server by the server name. */
1598 SilcServerEntry silc_client_get_server(SilcClient client,
1599 SilcClientConnection conn,
1602 SilcIDCacheEntry id_cache;
1603 SilcServerEntry entry;
1605 if (!client || !conn || !server_name)
1608 SILC_LOG_DEBUG(("Find server by name %s", server_name));
1610 /* Normalize server name for search */
1611 server_name = silc_identifier_check(server_name, strlen(server_name),
1612 SILC_STRING_UTF8, 256, NULL);
1616 silc_mutex_lock(conn->internal->lock);
1618 if (!silc_idcache_find_by_name_one(conn->internal->server_cache,
1619 server_name, &id_cache)) {
1620 silc_free(server_name);
1621 silc_mutex_unlock(conn->internal->lock);
1625 SILC_LOG_DEBUG(("Found"));
1628 entry = id_cache->context;
1629 silc_client_ref_server(client, conn, entry);
1631 silc_mutex_unlock(conn->internal->lock);
1633 silc_free(server_name);
1638 /* Finds entry for server by the server ID. */
1640 SilcServerEntry silc_client_get_server_by_id(SilcClient client,
1641 SilcClientConnection conn,
1642 SilcServerID *server_id)
1644 SilcIDCacheEntry id_cache;
1645 SilcServerEntry entry;
1647 if (!client || !conn || !server_id)
1650 SILC_LOG_DEBUG(("Find server by id %s",
1651 silc_id_render(server_id, SILC_ID_SERVER)));
1653 silc_mutex_lock(conn->internal->lock);
1655 if (!silc_idcache_find_by_id_one(conn->internal->server_cache,
1656 server_id, &id_cache)) {
1657 silc_mutex_unlock(conn->internal->lock);
1661 SILC_LOG_DEBUG(("Found"));
1664 entry = id_cache->context;
1665 silc_client_ref_server(client, conn, entry);
1667 silc_mutex_unlock(conn->internal->lock);
1672 /*********************** Server Resolving from Server ***********************/
1674 /* Resolving context */
1677 SilcGetServerCallback completion;
1679 } *SilcClientGetServerInternal;
1681 /* Resolving command callback */
1683 static SilcBool silc_client_get_server_cb(SilcClient client,
1684 SilcClientConnection conn,
1685 SilcCommand command,
1691 SilcClientGetServerInternal i = context;
1692 SilcServerEntry server;
1694 if (error != SILC_STATUS_OK) {
1695 SILC_LOG_DEBUG(("Resolving failed: %s", silc_get_status_message(error)));
1697 i->completion(client, conn, error, NULL, i->context);
1701 /* Add the returned servers to list */
1702 if (i->completion) {
1703 server = va_arg(ap, SilcServerEntry);
1704 silc_client_ref_server(client, conn, server);
1705 silc_dlist_add(i->servers, server);
1706 server->internal.resolve_cmd_ident = 0;
1709 if (status == SILC_STATUS_OK || status == SILC_STATUS_LIST_END) {
1710 /* Deliver the servers to the caller */
1711 if (i->completion) {
1712 SILC_LOG_DEBUG(("Resolved %d servers", silc_dlist_count(i->servers)));
1713 silc_dlist_start(i->servers);
1714 i->completion(client, conn, SILC_STATUS_OK, i->servers, i->context);
1722 silc_client_list_free_servers(client, conn, i->servers);
1727 /* Resolve server by server ID */
1730 silc_client_get_server_by_id_resolve(SilcClient client,
1731 SilcClientConnection conn,
1732 SilcServerID *server_id,
1733 SilcGetServerCallback completion,
1736 SilcClientGetServerInternal i;
1737 SilcServerEntry server;
1739 SilcUInt16 cmd_ident;
1741 if (!client || !conn || !server_id || !completion)
1744 SILC_LOG_DEBUG(("Resolve server by id %s",
1745 silc_id_render(server_id, SILC_ID_SERVER)));
1747 i = silc_calloc(1, sizeof(*i));
1750 i->completion = completion;
1751 i->context = context;
1752 i->servers = silc_dlist_init();
1758 /* Attach to resolving, if on going */
1759 server = silc_client_get_server_by_id(client, conn, server_id);
1760 if (server && server->internal.resolve_cmd_ident) {
1761 SILC_LOG_DEBUG(("Attach to existing resolving"));
1762 silc_client_unref_server(client, conn, server);
1763 silc_client_command_pending(conn, SILC_COMMAND_NONE,
1764 server->internal.resolve_cmd_ident,
1765 silc_client_get_server_cb, i);
1766 return server->internal.resolve_cmd_ident;
1769 /* Send the command */
1770 idp = silc_id_payload_encode(server_id, SILC_ID_SERVER);
1771 cmd_ident = silc_client_command_send(client, conn, SILC_COMMAND_IDENTIFY,
1772 silc_client_get_server_cb, i, 1,
1773 5, silc_buffer_datalen(idp));
1774 silc_buffer_free(idp);
1775 if (!cmd_ident && completion)
1776 completion(client, conn, SILC_STATUS_ERR_RESOURCE_LIMIT, NULL, context);
1778 if (server && cmd_ident)
1779 server->internal.resolve_cmd_ident = cmd_ident;
1781 silc_client_unref_server(client, conn, server);
1786 /************************** Server Entry Routines ***************************/
1788 /* Add new server entry */
1790 SilcServerEntry silc_client_add_server(SilcClient client,
1791 SilcClientConnection conn,
1792 const char *server_name,
1793 const char *server_info,
1794 SilcServerID *server_id)
1796 SilcServerEntry server_entry;
1797 char *server_namec = NULL;
1802 SILC_LOG_DEBUG(("Adding new server %s", server_name));
1804 server_entry = silc_calloc(1, sizeof(*server_entry));
1808 silc_atomic_init8(&server_entry->internal.refcnt, 0);
1809 server_entry->id = *server_id;
1811 server_entry->server_name = strdup(server_name);
1813 server_entry->server_info = strdup(server_info);
1815 /* Normalize server name */
1817 server_namec = silc_identifier_check(server_name, strlen(server_name),
1818 SILC_STRING_UTF8, 256, NULL);
1819 if (!server_namec) {
1820 silc_free(server_entry->server_name);
1821 silc_free(server_entry->server_info);
1822 silc_free(server_entry);
1827 silc_mutex_lock(conn->internal->lock);
1829 /* Add server to cache */
1830 if (!silc_idcache_add(conn->internal->server_cache, server_namec,
1831 &server_entry->id, server_entry)) {
1832 silc_free(server_namec);
1833 silc_free(server_entry->server_name);
1834 silc_free(server_entry->server_info);
1835 silc_free(server_entry);
1836 silc_mutex_unlock(conn->internal->lock);
1840 silc_mutex_unlock(conn->internal->lock);
1841 silc_client_ref_server(client, conn, server_entry);
1843 SILC_LOG_DEBUG(("Added %p", server_entry));
1845 return server_entry;
1848 /* Removes server from the cache by the server entry. */
1850 SilcBool silc_client_del_server(SilcClient client, SilcClientConnection conn,
1851 SilcServerEntry server)
1858 if (silc_atomic_sub_int8(&server->internal.refcnt, 1) > 0)
1861 SILC_LOG_DEBUG(("Deleting server %p", server));
1863 silc_mutex_lock(conn->internal->lock);
1864 ret = silc_idcache_del_by_context(conn->internal->server_cache,
1866 silc_mutex_unlock(conn->internal->lock);
1868 silc_free(server->server_name);
1869 silc_free(server->server_info);
1870 if (server->public_key)
1871 silc_pkcs_public_key_free(server->public_key);
1872 silc_atomic_uninit8(&server->internal.refcnt);
1878 /* Updates the `server_entry' with the new information sent as argument. */
1880 void silc_client_update_server(SilcClient client,
1881 SilcClientConnection conn,
1882 SilcServerEntry server_entry,
1883 const char *server_name,
1884 const char *server_info)
1886 char *server_namec = NULL;
1888 SILC_LOG_DEBUG(("Updating server %p", server_entry));
1891 (!server_entry->server_name ||
1892 !silc_utf8_strcasecmp(server_entry->server_name, server_name))) {
1894 server_namec = silc_identifier_check(server_name, strlen(server_name),
1895 SILC_STRING_UTF8, 256, NULL);
1899 silc_free(server_entry->server_name);
1900 server_entry->server_name = strdup(server_name);
1901 if (!server_entry->server_name)
1904 /* Update cache entry */
1905 silc_mutex_lock(conn->internal->lock);
1906 silc_idcache_update_by_context(conn->internal->server_cache, server_entry,
1907 NULL, server_namec, TRUE);
1908 silc_mutex_unlock(conn->internal->lock);
1912 (!server_entry->server_info ||
1913 !silc_utf8_strcasecmp(server_entry->server_info, server_info))) {
1914 silc_free(server_entry->server_info);
1915 server_entry->server_info = strdup(server_info);
1919 /* Take reference of server entry */
1921 void silc_client_ref_server(SilcClient client, SilcClientConnection conn,
1922 SilcServerEntry server_entry)
1924 silc_atomic_add_int8(&server_entry->internal.refcnt, 1);
1925 SILC_LOG_DEBUG(("Server %p refcnt %d->%d", server_entry,
1926 silc_atomic_get_int8(&server_entry->internal.refcnt) - 1,
1927 silc_atomic_get_int8(&server_entry->internal.refcnt)));
1930 /* Release reference of server entry */
1932 void silc_client_unref_server(SilcClient client, SilcClientConnection conn,
1933 SilcServerEntry server_entry)
1936 SILC_LOG_DEBUG(("Server %p refcnt %d->%d", server_entry,
1937 silc_atomic_get_int8(&server_entry->internal.refcnt),
1938 silc_atomic_get_int8(&server_entry->internal.refcnt)
1940 silc_client_del_server(client, conn, server_entry);
1944 /* Free server entry list */
1946 void silc_client_list_free_servers(SilcClient client,
1947 SilcClientConnection conn,
1948 SilcDList server_list)
1950 SilcServerEntry server_entry;
1953 silc_dlist_start(server_list);
1954 while ((server_entry = silc_dlist_get(server_list)))
1955 silc_client_unref_server(client, conn, server_entry);
1957 silc_dlist_uninit(server_list);