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(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_rwlock_alloc(&client_entry->internal.lock);
700 silc_atomic_init8(&client_entry->internal.refcnt, 0);
701 client_entry->id = *id;
702 client_entry->internal.valid = TRUE;
703 client_entry->mode = mode;
704 client_entry->realname = userinfo ? strdup(userinfo) : NULL;
705 silc_parse_userfqdn(nickname, client_entry->nickname,
706 sizeof(client_entry->nickname),
707 client_entry->server,
708 sizeof(client_entry->server));
709 silc_parse_userfqdn(username, client_entry->username,
710 sizeof(client_entry->username),
711 client_entry->hostname,
712 sizeof(client_entry->hostname));
713 client_entry->channels = silc_hash_table_alloc(1, silc_hash_ptr, NULL, NULL,
714 NULL, NULL, NULL, TRUE);
715 if (!client_entry->channels) {
716 silc_free(client_entry->realname);
717 silc_free(client_entry);
721 /* Normalize nickname */
722 if (client_entry->nickname[0]) {
723 nick = silc_identifier_check(client_entry->nickname,
724 strlen(client_entry->nickname),
725 SILC_STRING_UTF8, 128, NULL);
727 silc_free(client_entry->realname);
728 silc_hash_table_free(client_entry->channels);
729 silc_free(client_entry);
734 silc_mutex_lock(conn->internal->lock);
736 /* Add client to cache, the normalized nickname is saved to cache */
737 if (!silc_idcache_add(conn->internal->client_cache, nick,
738 &client_entry->id, client_entry)) {
740 silc_free(client_entry->realname);
741 silc_hash_table_free(client_entry->channels);
742 silc_free(client_entry);
743 silc_mutex_unlock(conn->internal->lock);
747 client_entry->nickname_normalized = nick;
749 silc_mutex_unlock(conn->internal->lock);
750 silc_client_ref_client(client, conn, client_entry);
752 /* Format the nickname */
753 silc_client_nickname_format(client, conn, client_entry, FALSE);
755 SILC_LOG_DEBUG(("Added %p", client_entry));
760 /* Updates the `client_entry' with the new information sent as argument.
761 This handles entry locking internally. */
763 void silc_client_update_client(SilcClient client,
764 SilcClientConnection conn,
765 SilcClientEntry client_entry,
766 const char *nickname,
767 const char *username,
768 const char *userinfo,
773 SILC_LOG_DEBUG(("Update client entry"));
775 silc_rwlock_wrlock(client_entry->internal.lock);
777 if (!client_entry->realname && userinfo)
778 client_entry->realname = strdup(userinfo);
779 if ((!client_entry->username[0] || !client_entry->hostname[0]) && username)
780 silc_parse_userfqdn(username, client_entry->username,
781 sizeof(client_entry->username),
782 client_entry->hostname,
783 sizeof(client_entry->username));
784 if (!client_entry->nickname[0] && nickname) {
785 silc_parse_userfqdn(nickname, client_entry->nickname,
786 sizeof(client_entry->nickname),
787 client_entry->server,
788 sizeof(client_entry->server));
790 /* Normalize nickname */
791 nick = silc_identifier_check(client_entry->nickname,
792 strlen(client_entry->nickname),
793 SILC_STRING_UTF8, 128, NULL);
795 silc_rwlock_unlock(client_entry->internal.lock);
799 /* Format nickname */
800 silc_client_nickname_format(client, conn, client_entry,
801 client_entry == conn->local_entry);
803 /* Update cache entry */
804 silc_mutex_lock(conn->internal->lock);
805 silc_idcache_update_by_context(conn->internal->client_cache,
806 client_entry, NULL, nick, TRUE);
807 silc_mutex_unlock(conn->internal->lock);
808 client_entry->nickname_normalized = nick;
810 client_entry->mode = mode;
812 silc_rwlock_unlock(client_entry->internal.lock);
815 /* Change a client's nickname. Must be called with `client_entry' locked. */
817 SilcBool silc_client_change_nickname(SilcClient client,
818 SilcClientConnection conn,
819 SilcClientEntry client_entry,
820 const char *new_nick,
821 SilcClientID *new_id,
822 const unsigned char *idp,
827 SILC_LOG_DEBUG(("Change nickname %s to %s", client_entry->nickname,
830 /* Normalize nickname */
831 tmp = silc_identifier_check(new_nick, strlen(new_nick),
832 SILC_STRING_UTF8, 128, NULL);
836 /* Update the client entry */
837 silc_mutex_lock(conn->internal->lock);
838 if (!silc_idcache_update_by_context(conn->internal->client_cache,
839 client_entry, new_id, tmp, TRUE)) {
841 silc_mutex_unlock(conn->internal->lock);
844 silc_mutex_unlock(conn->internal->lock);
846 memset(client_entry->nickname, 0, sizeof(client_entry->nickname));
847 memcpy(client_entry->nickname, new_nick, strlen(new_nick));
848 client_entry->nickname_normalized = tmp;
849 silc_client_nickname_format(client, conn, client_entry,
850 client_entry == conn->local_entry);
852 /* For my client entry, update ID and set new ID to packet stream */
853 if (client_entry == conn->local_entry) {
854 if (idp && idp_len) {
855 silc_buffer_enlarge(conn->internal->local_idp, idp_len);
856 silc_buffer_put(conn->internal->local_idp, idp, idp_len);
859 silc_packet_set_ids(conn->stream, SILC_ID_CLIENT, conn->local_id,
866 /* Deletes the client entry and frees all memory. */
868 void silc_client_del_client_entry(SilcClient client,
869 SilcClientConnection conn,
870 SilcClientEntry client_entry)
872 silc_free(client_entry->realname);
873 silc_free(client_entry->nickname_normalized);
874 silc_free(client_entry->internal.key);
875 if (client_entry->public_key)
876 silc_pkcs_public_key_free(client_entry->public_key);
877 silc_hash_table_free(client_entry->channels);
878 if (client_entry->internal.send_key)
879 silc_cipher_free(client_entry->internal.send_key);
880 if (client_entry->internal.receive_key)
881 silc_cipher_free(client_entry->internal.receive_key);
882 if (client_entry->internal.hmac_send)
883 silc_hmac_free(client_entry->internal.hmac_send);
884 if (client_entry->internal.hmac_receive)
885 silc_hmac_free(client_entry->internal.hmac_receive);
887 silc_client_ftp_session_free_client(conn, client_entry);
888 if (client_entry->internal->ke)
889 silc_client_abort_key_agreement(client, conn, client_entry);
891 silc_atomic_uninit8(&client_entry->internal.refcnt);
892 silc_rwlock_free(client_entry->internal.lock);
893 silc_free(client_entry);
896 /* Removes client from the cache by the client entry. */
898 SilcBool silc_client_del_client(SilcClient client, SilcClientConnection conn,
899 SilcClientEntry client_entry)
906 if (silc_atomic_sub_int8(&client_entry->internal.refcnt, 1) > 0)
909 SILC_LOG_DEBUG(("Deleting client %p", client_entry));
911 silc_mutex_lock(conn->internal->lock);
912 ret = silc_idcache_del_by_context(conn->internal->client_cache,
914 silc_mutex_unlock(conn->internal->lock);
917 /* Remove from channels */
918 silc_client_remove_from_channels(client, conn, client_entry);
920 /* Free the client entry data */
921 silc_client_del_client_entry(client, conn, client_entry);
929 void silc_client_lock_client(SilcClientEntry client_entry)
931 silc_rwlock_rdlock(client_entry->internal.lock);
936 void silc_client_unlock_client(SilcClientEntry client_entry)
938 silc_rwlock_unlock(client_entry->internal.lock);
941 /* Take reference of client entry */
943 SilcClientEntry silc_client_ref_client(SilcClient client,
944 SilcClientConnection conn,
945 SilcClientEntry client_entry)
947 silc_atomic_add_int8(&client_entry->internal.refcnt, 1);
948 SILC_LOG_DEBUG(("Client %p refcnt %d->%d", client_entry,
949 silc_atomic_get_int8(&client_entry->internal.refcnt) - 1,
950 silc_atomic_get_int8(&client_entry->internal.refcnt)));
954 /* Release reference of client entry */
956 void silc_client_unref_client(SilcClient client, SilcClientConnection conn,
957 SilcClientEntry client_entry)
960 SILC_LOG_DEBUG(("Client %p refcnt %d->%d", client_entry,
961 silc_atomic_get_int8(&client_entry->internal.refcnt),
962 silc_atomic_get_int8(&client_entry->internal.refcnt) - 1));
963 silc_client_del_client(client, conn, client_entry);
967 /* Free client entry list */
969 void silc_client_list_free(SilcClient client, SilcClientConnection conn,
970 SilcDList client_list)
972 SilcClientEntry client_entry;
975 silc_dlist_start(client_list);
976 while ((client_entry = silc_dlist_get(client_list)))
977 silc_client_unref_client(client, conn, client_entry);
979 silc_dlist_uninit(client_list);
983 /* Formats the nickname of the client specified by the `client_entry'.
984 If the format is specified by the application this will format the
985 nickname and replace the old nickname in the client entry. If the
986 format string is not specified then this function has no effect.
987 Returns the client entry that was formatted. */
989 SilcClientEntry silc_client_nickname_format(SilcClient client,
990 SilcClientConnection conn,
991 SilcClientEntry client_entry,
995 char newnick[128 + 1];
999 SilcClientEntry entry, unformatted = NULL;
1001 SILC_LOG_DEBUG(("Format nickname"));
1003 if (!client->internal->params->nickname_format[0])
1004 return client_entry;
1005 if (!client_entry->nickname[0])
1008 /* Get all clients with same nickname. Do not perform the formatting
1009 if there aren't any clients with same nickname unless the application
1010 is forcing us to do so. */
1011 clients = silc_client_get_clients_local(client, conn,
1012 client_entry->nickname, NULL);
1015 if (silc_dlist_count(clients) == 1 &&
1016 !client->internal->params->nickname_force_format) {
1017 silc_client_list_free(client, conn, clients);
1018 return client_entry;
1023 while ((entry = silc_dlist_get(clients))) {
1024 if (entry->internal.valid && entry != client_entry)
1026 if (entry->internal.valid && entry != client_entry &&
1027 silc_utf8_strcasecmp(entry->nickname, client_entry->nickname)) {
1029 unformatted = entry;
1033 if (!len || freebase) {
1034 silc_client_list_free(client, conn, clients);
1035 return client_entry;
1038 /* If priority formatting, this client always gets unformatted nickname. */
1039 if (unformatted && priority)
1040 client_entry = unformatted;
1042 memset(newnick, 0, sizeof(newnick));
1043 cp = client->internal->params->nickname_format;
1053 if (!client_entry->nickname[0])
1055 len = strlen(client_entry->nickname);
1056 memcpy(&newnick[off], client_entry->nickname, len);
1060 /* Stripped hostname */
1061 if (!client_entry->hostname[0])
1063 len = strcspn(client_entry->hostname, ".");
1064 i = strcspn(client_entry->hostname, "-");
1067 memcpy(&newnick[off], client_entry->hostname, len);
1072 if (!client_entry->hostname[0])
1074 len = strlen(client_entry->hostname);
1075 memcpy(&newnick[off], client_entry->hostname, len);
1079 /* Stripped server name */
1080 if (!client_entry->server)
1082 len = strcspn(client_entry->server, ".");
1083 memcpy(&newnick[off], client_entry->server, len);
1087 /* Full server name */
1088 if (!client_entry->server)
1090 len = strlen(client_entry->server);
1091 memcpy(&newnick[off], client_entry->server, len);
1095 /* Ascending number */
1100 if (silc_dlist_count(clients) == 1)
1103 silc_dlist_start(clients);
1104 while ((entry = silc_dlist_get(clients))) {
1105 if (!silc_utf8_strncasecmp(entry->nickname, newnick, off))
1107 if (strlen(entry->nickname) <= off)
1109 num = atoi(&entry->nickname[off]);
1114 memset(tmp, 0, sizeof(tmp));
1115 silc_snprintf(tmp, sizeof(tmp) - 1, "%d", ++max);
1117 memcpy(&newnick[off], tmp, len);
1122 /* Some other character in the string */
1123 memcpy(&newnick[off], cp, 1);
1132 memcpy(client_entry->nickname, newnick, strlen(newnick));
1133 silc_client_list_free(client, conn, clients);
1135 return client_entry;
1138 /* Parses nickname according to nickname format string */
1140 SilcBool silc_client_nickname_parse(SilcClient client,
1141 SilcClientConnection conn,
1145 char *cp, s = 0, e = 0, *nick;
1149 if (!client->internal->params->nickname_format[0])
1152 if (!nickname || !nickname[0])
1155 cp = client->internal->params->nickname_format;
1175 /* Get separator character */
1188 /* Parse the nickname */
1192 if (strchr(nickname, s))
1193 nick = strchr(nickname, s) + 1;
1195 if (strchr(nick, e))
1196 len = strchr(nick, e) - nick;
1200 *ret_nick = silc_memdup(nick, len);
1204 SILC_LOG_DEBUG(("Parsed nickname: %s", *ret_nick));
1209 /************************ Channel Searching Locally *************************/
1211 /* Finds entry for channel by the channel name. Returns the entry or NULL
1212 if the entry was not found. It is found only if the client is joined
1215 SilcChannelEntry silc_client_get_channel(SilcClient client,
1216 SilcClientConnection conn,
1219 SilcIDCacheEntry id_cache;
1220 SilcChannelEntry entry;
1222 if (!client || !conn || !channel)
1225 SILC_LOG_DEBUG(("Find channel %s", channel));
1227 /* Normalize name for search */
1228 channel = silc_channel_name_check(channel, strlen(channel), SILC_STRING_UTF8,
1233 silc_mutex_lock(conn->internal->lock);
1235 if (!silc_idcache_find_by_name_one(conn->internal->channel_cache, channel,
1237 silc_mutex_unlock(conn->internal->lock);
1242 SILC_LOG_DEBUG(("Found"));
1244 entry = id_cache->context;
1247 silc_client_ref_channel(client, conn, entry);
1248 silc_mutex_unlock(conn->internal->lock);
1255 /* Finds entry for channel by the channel ID. Returns the entry or NULL
1256 if the entry was not found. It is found only if the client is joined
1259 SilcChannelEntry silc_client_get_channel_by_id(SilcClient client,
1260 SilcClientConnection conn,
1261 SilcChannelID *channel_id)
1263 SilcIDCacheEntry id_cache;
1264 SilcChannelEntry entry;
1266 if (!client || !conn || !channel_id)
1269 SILC_LOG_DEBUG(("Find channel by id %s",
1270 silc_id_render(channel_id, SILC_ID_CHANNEL)));
1272 silc_mutex_lock(conn->internal->lock);
1274 if (!silc_idcache_find_by_id_one(conn->internal->channel_cache, channel_id,
1276 silc_mutex_unlock(conn->internal->lock);
1280 SILC_LOG_DEBUG(("Found"));
1282 entry = id_cache->context;
1285 silc_client_ref_channel(client, conn, entry);
1286 silc_mutex_unlock(conn->internal->lock);
1291 /********************** Channel Resolving from Server ***********************/
1293 /* Channel resolving context */
1296 SilcGetChannelCallback completion;
1298 } *SilcClientGetChannelInternal;
1300 /* Resolving command callback */
1302 static SilcBool silc_client_get_channel_cb(SilcClient client,
1303 SilcClientConnection conn,
1304 SilcCommand command,
1310 SilcClientGetChannelInternal i = context;
1311 SilcChannelEntry entry;
1313 if (error != SILC_STATUS_OK) {
1314 SILC_LOG_DEBUG(("Resolving failed: %s", silc_get_status_message(error)));
1316 i->completion(client, conn, error, NULL, i->context);
1320 /* Add the returned channel to list */
1321 if (i->completion) {
1322 entry = va_arg(ap, SilcChannelEntry);
1323 silc_client_ref_channel(client, conn, entry);
1324 silc_dlist_add(i->channels, entry);
1327 if (status == SILC_STATUS_OK || status == SILC_STATUS_LIST_END) {
1328 /* Deliver the channels to the caller */
1329 if (i->completion) {
1330 SILC_LOG_DEBUG(("Resolved %d channels", silc_dlist_count(i->channels)));
1331 silc_dlist_start(i->channels);
1332 i->completion(client, conn, SILC_STATUS_OK, i->channels, i->context);
1340 silc_client_list_free_channels(client, conn, i->channels);
1345 /* Resolves channel entry from the server by the channel name. */
1347 void silc_client_get_channel_resolve(SilcClient client,
1348 SilcClientConnection conn,
1350 SilcGetChannelCallback completion,
1353 SilcClientGetChannelInternal i;
1355 if (!client || !conn || !channel_name || !completion)
1358 SILC_LOG_DEBUG(("Resolve channel %s", channel_name));
1360 i = silc_calloc(1, sizeof(*i));
1363 i->completion = completion;
1364 i->context = context;
1365 i->channels = silc_dlist_init();
1371 /* Send the command */
1372 if (!silc_client_command_send(client, conn, SILC_COMMAND_IDENTIFY,
1373 silc_client_get_channel_cb, i, 1,
1374 3, channel_name, strlen(channel_name))) {
1376 completion(client, conn, SILC_STATUS_ERR_RESOURCE_LIMIT, NULL, context);
1380 /* Resolves channel information from the server by the channel ID. */
1383 silc_client_get_channel_by_id_resolve(SilcClient client,
1384 SilcClientConnection conn,
1385 SilcChannelID *channel_id,
1386 SilcGetChannelCallback completion,
1389 SilcClientGetChannelInternal i;
1391 SilcUInt16 cmd_ident;
1393 if (!client || !conn || !channel_id || !completion)
1396 SILC_LOG_DEBUG(("Resolve channel by id %s",
1397 silc_id_render(channel_id, SILC_ID_CHANNEL)));
1399 i = silc_calloc(1, sizeof(*i));
1402 i->completion = completion;
1403 i->context = context;
1404 i->channels = silc_dlist_init();
1410 /* Send the command */
1411 idp = silc_id_payload_encode(channel_id, SILC_ID_CHANNEL);
1412 cmd_ident = silc_client_command_send(client, conn, SILC_COMMAND_IDENTIFY,
1413 silc_client_get_channel_cb, i, 1,
1414 5, silc_buffer_datalen(idp));
1415 silc_buffer_free(idp);
1416 if (!cmd_ident && completion)
1417 completion(client, conn, SILC_STATUS_ERR_RESOURCE_LIMIT, NULL, context);
1422 /************************* Channel Entry Routines ***************************/
1424 /* Add new channel entry to the ID Cache */
1426 SilcChannelEntry silc_client_add_channel(SilcClient client,
1427 SilcClientConnection conn,
1428 const char *channel_name,
1430 SilcChannelID *channel_id)
1432 SilcChannelEntry channel;
1433 char *channel_namec;
1435 SILC_LOG_DEBUG(("Start"));
1437 channel = silc_calloc(1, sizeof(*channel));
1441 silc_rwlock_alloc(&channel->internal.lock);
1442 silc_atomic_init16(&channel->internal.refcnt, 0);
1443 channel->id = *channel_id;
1444 channel->mode = mode;
1446 channel->channel_name = strdup(channel_name);
1447 if (!channel->channel_name) {
1452 channel->user_list = silc_hash_table_alloc(1, silc_hash_ptr, NULL, NULL,
1453 NULL, NULL, NULL, TRUE);
1454 if (!channel->user_list) {
1455 silc_free(channel->channel_name);
1460 /* Normalize channel name */
1461 channel_namec = silc_channel_name_check(channel_name, strlen(channel_name),
1462 SILC_STRING_UTF8, 256, NULL);
1463 if (!channel_namec) {
1464 silc_free(channel->channel_name);
1465 silc_hash_table_free(channel->user_list);
1470 silc_mutex_lock(conn->internal->lock);
1472 /* Add channel to cache, the normalized channel name is saved to cache */
1473 if (!silc_idcache_add(conn->internal->channel_cache, channel_namec,
1474 &channel->id, channel)) {
1475 silc_free(channel_namec);
1476 silc_free(channel->channel_name);
1477 silc_hash_table_free(channel->user_list);
1479 silc_mutex_unlock(conn->internal->lock);
1483 silc_mutex_unlock(conn->internal->lock);
1484 silc_client_ref_channel(client, conn, channel);
1486 SILC_LOG_DEBUG(("Added %p", channel));
1491 /* Removes channel from the cache by the channel entry. */
1493 SilcBool silc_client_del_channel(SilcClient client, SilcClientConnection conn,
1494 SilcChannelEntry channel)
1503 if (silc_atomic_sub_int16(&channel->internal.refcnt, 1) > 0)
1506 SILC_LOG_DEBUG(("Deleting channel %p", channel));
1508 silc_mutex_lock(conn->internal->lock);
1509 ret = silc_idcache_del_by_context(conn->internal->channel_cache,
1511 silc_mutex_unlock(conn->internal->lock);
1516 silc_client_empty_channel(client, conn, channel);
1517 silc_hash_table_free(channel->user_list);
1518 silc_free(channel->channel_name);
1519 silc_free(channel->topic);
1520 if (channel->founder_key)
1521 silc_pkcs_public_key_free(channel->founder_key);
1522 if (channel->internal.send_key)
1523 silc_cipher_free(channel->internal.send_key);
1524 if (channel->internal.receive_key)
1525 silc_cipher_free(channel->internal.receive_key);
1526 if (channel->internal.hmac)
1527 silc_hmac_free(channel->internal.hmac);
1528 if (channel->internal.old_channel_keys) {
1529 silc_dlist_start(channel->internal.old_channel_keys);
1530 while ((key = silc_dlist_get(channel->internal.old_channel_keys)))
1531 silc_cipher_free(key);
1532 silc_dlist_uninit(channel->internal.old_channel_keys);
1534 if (channel->internal.old_hmacs) {
1535 silc_dlist_start(channel->internal.old_hmacs);
1536 while ((hmac = silc_dlist_get(channel->internal.old_hmacs)))
1537 silc_hmac_free(hmac);
1538 silc_dlist_uninit(channel->internal.old_hmacs);
1540 if (channel->channel_pubkeys)
1541 silc_argument_list_free(channel->channel_pubkeys,
1542 SILC_ARGUMENT_PUBLIC_KEY);
1543 silc_client_del_channel_private_keys(client, conn, channel);
1544 silc_atomic_uninit16(&channel->internal.refcnt);
1545 silc_rwlock_free(channel->internal.lock);
1546 silc_schedule_task_del_by_context(conn->client->schedule, channel);
1552 /* Replaces the channel ID of the `channel' to `new_id'. Returns FALSE
1553 if the ID could not be changed. This handles entry locking internally. */
1555 SilcBool silc_client_replace_channel_id(SilcClient client,
1556 SilcClientConnection conn,
1557 SilcChannelEntry channel,
1558 SilcChannelID *new_id)
1560 SilcBool ret = FALSE;
1565 SILC_LOG_DEBUG(("Old Channel ID id(%s)",
1566 silc_id_render(&channel->id, SILC_ID_CHANNEL)));
1567 SILC_LOG_DEBUG(("New Channel ID id(%s)",
1568 silc_id_render(new_id, SILC_ID_CHANNEL)));
1571 silc_rwlock_wrlock(channel->internal.lock);
1572 silc_mutex_lock(conn->internal->lock);
1573 silc_idcache_update_by_context(conn->internal->channel_cache, channel,
1574 new_id, NULL, FALSE);
1575 silc_mutex_unlock(conn->internal->lock);
1576 silc_rwlock_unlock(channel->internal.lock);
1583 void silc_client_lock_channel(SilcChannelEntry channel_entry)
1585 silc_rwlock_rdlock(channel_entry->internal.lock);
1590 void silc_client_unlock_channel(SilcChannelEntry channel_entry)
1592 silc_rwlock_unlock(channel_entry->internal.lock);
1595 /* Take reference of channel entry */
1597 SilcChannelEntry silc_client_ref_channel(SilcClient client,
1598 SilcClientConnection conn,
1599 SilcChannelEntry channel_entry)
1601 silc_atomic_add_int16(&channel_entry->internal.refcnt, 1);
1602 SILC_LOG_DEBUG(("Channel %p refcnt %d->%d", channel_entry,
1603 silc_atomic_get_int16(&channel_entry->internal.refcnt) - 1,
1604 silc_atomic_get_int16(&channel_entry->internal.refcnt)));
1605 return channel_entry;
1608 /* Release reference of channel entry */
1610 void silc_client_unref_channel(SilcClient client, SilcClientConnection conn,
1611 SilcChannelEntry channel_entry)
1613 if (channel_entry) {
1614 SILC_LOG_DEBUG(("Channel %p refcnt %d->%d", channel_entry,
1615 silc_atomic_get_int16(&channel_entry->internal.refcnt),
1616 silc_atomic_get_int16(&channel_entry->internal.refcnt)
1618 silc_client_del_channel(client, conn, channel_entry);
1622 /* Free channel entry list */
1624 void silc_client_list_free_channels(SilcClient client,
1625 SilcClientConnection conn,
1626 SilcDList channel_list)
1628 SilcChannelEntry channel_entry;
1631 silc_dlist_start(channel_list);
1632 while ((channel_entry = silc_dlist_get(channel_list)))
1633 silc_client_unref_channel(client, conn, channel_entry);
1635 silc_dlist_uninit(channel_list);
1639 /************************* Server Searching Locally *************************/
1641 /* Finds entry for server by the server name. */
1643 SilcServerEntry silc_client_get_server(SilcClient client,
1644 SilcClientConnection conn,
1647 SilcIDCacheEntry id_cache;
1648 SilcServerEntry entry;
1650 if (!client || !conn || !server_name)
1653 SILC_LOG_DEBUG(("Find server by name %s", server_name));
1655 /* Normalize server name for search */
1656 server_name = silc_identifier_check(server_name, strlen(server_name),
1657 SILC_STRING_UTF8, 256, NULL);
1661 silc_mutex_lock(conn->internal->lock);
1663 if (!silc_idcache_find_by_name_one(conn->internal->server_cache,
1664 server_name, &id_cache)) {
1665 silc_free(server_name);
1666 silc_mutex_unlock(conn->internal->lock);
1670 SILC_LOG_DEBUG(("Found"));
1673 entry = id_cache->context;
1674 silc_client_ref_server(client, conn, entry);
1676 silc_mutex_unlock(conn->internal->lock);
1678 silc_free(server_name);
1683 /* Finds entry for server by the server ID. */
1685 SilcServerEntry silc_client_get_server_by_id(SilcClient client,
1686 SilcClientConnection conn,
1687 SilcServerID *server_id)
1689 SilcIDCacheEntry id_cache;
1690 SilcServerEntry entry;
1692 if (!client || !conn || !server_id)
1695 SILC_LOG_DEBUG(("Find server by id %s",
1696 silc_id_render(server_id, SILC_ID_SERVER)));
1698 silc_mutex_lock(conn->internal->lock);
1700 if (!silc_idcache_find_by_id_one(conn->internal->server_cache,
1701 server_id, &id_cache)) {
1702 silc_mutex_unlock(conn->internal->lock);
1706 SILC_LOG_DEBUG(("Found"));
1709 entry = id_cache->context;
1710 silc_client_ref_server(client, conn, entry);
1712 silc_mutex_unlock(conn->internal->lock);
1717 /*********************** Server Resolving from Server ***********************/
1719 /* Resolving context */
1722 SilcGetServerCallback completion;
1724 } *SilcClientGetServerInternal;
1726 /* Resolving command callback */
1728 static SilcBool silc_client_get_server_cb(SilcClient client,
1729 SilcClientConnection conn,
1730 SilcCommand command,
1736 SilcClientGetServerInternal i = context;
1737 SilcServerEntry server;
1739 if (error != SILC_STATUS_OK) {
1740 SILC_LOG_DEBUG(("Resolving failed: %s", silc_get_status_message(error)));
1742 i->completion(client, conn, error, NULL, i->context);
1746 /* Add the returned servers to list */
1747 if (i->completion) {
1748 server = va_arg(ap, SilcServerEntry);
1749 silc_client_ref_server(client, conn, server);
1750 silc_dlist_add(i->servers, server);
1751 server->internal.resolve_cmd_ident = 0;
1754 if (status == SILC_STATUS_OK || status == SILC_STATUS_LIST_END) {
1755 /* Deliver the servers to the caller */
1756 if (i->completion) {
1757 SILC_LOG_DEBUG(("Resolved %d servers", silc_dlist_count(i->servers)));
1758 silc_dlist_start(i->servers);
1759 i->completion(client, conn, SILC_STATUS_OK, i->servers, i->context);
1767 silc_client_list_free_servers(client, conn, i->servers);
1772 /* Resolve server by server ID */
1775 silc_client_get_server_by_id_resolve(SilcClient client,
1776 SilcClientConnection conn,
1777 SilcServerID *server_id,
1778 SilcGetServerCallback completion,
1781 SilcClientGetServerInternal i;
1782 SilcServerEntry server;
1784 SilcUInt16 cmd_ident;
1786 if (!client || !conn || !server_id || !completion)
1789 SILC_LOG_DEBUG(("Resolve server by id %s",
1790 silc_id_render(server_id, SILC_ID_SERVER)));
1792 i = silc_calloc(1, sizeof(*i));
1795 i->completion = completion;
1796 i->context = context;
1797 i->servers = silc_dlist_init();
1803 /* Attach to resolving, if on going */
1804 server = silc_client_get_server_by_id(client, conn, server_id);
1805 if (server && server->internal.resolve_cmd_ident) {
1806 SILC_LOG_DEBUG(("Attach to existing resolving"));
1807 silc_client_unref_server(client, conn, server);
1808 silc_client_command_pending(conn, SILC_COMMAND_NONE,
1809 server->internal.resolve_cmd_ident,
1810 silc_client_get_server_cb, i);
1811 return server->internal.resolve_cmd_ident;
1814 /* Send the command */
1815 idp = silc_id_payload_encode(server_id, SILC_ID_SERVER);
1816 cmd_ident = silc_client_command_send(client, conn, SILC_COMMAND_IDENTIFY,
1817 silc_client_get_server_cb, i, 1,
1818 5, silc_buffer_datalen(idp));
1819 silc_buffer_free(idp);
1820 if (!cmd_ident && completion)
1821 completion(client, conn, SILC_STATUS_ERR_RESOURCE_LIMIT, NULL, context);
1823 if (server && cmd_ident)
1824 server->internal.resolve_cmd_ident = cmd_ident;
1826 silc_client_unref_server(client, conn, server);
1831 /************************** Server Entry Routines ***************************/
1833 /* Add new server entry */
1835 SilcServerEntry silc_client_add_server(SilcClient client,
1836 SilcClientConnection conn,
1837 const char *server_name,
1838 const char *server_info,
1839 SilcServerID *server_id)
1841 SilcServerEntry server_entry;
1842 char *server_namec = NULL;
1847 SILC_LOG_DEBUG(("Adding new server %s", server_name));
1849 server_entry = silc_calloc(1, sizeof(*server_entry));
1853 silc_rwlock_alloc(&server_entry->internal.lock);
1854 silc_atomic_init8(&server_entry->internal.refcnt, 0);
1855 server_entry->id = *server_id;
1857 server_entry->server_name = strdup(server_name);
1859 server_entry->server_info = strdup(server_info);
1861 /* Normalize server name */
1863 server_namec = silc_identifier_check(server_name, strlen(server_name),
1864 SILC_STRING_UTF8, 256, NULL);
1865 if (!server_namec) {
1866 silc_free(server_entry->server_name);
1867 silc_free(server_entry->server_info);
1868 silc_free(server_entry);
1873 silc_mutex_lock(conn->internal->lock);
1875 /* Add server to cache */
1876 if (!silc_idcache_add(conn->internal->server_cache, server_namec,
1877 &server_entry->id, server_entry)) {
1878 silc_free(server_namec);
1879 silc_free(server_entry->server_name);
1880 silc_free(server_entry->server_info);
1881 silc_free(server_entry);
1882 silc_mutex_unlock(conn->internal->lock);
1886 silc_mutex_unlock(conn->internal->lock);
1887 silc_client_ref_server(client, conn, server_entry);
1889 SILC_LOG_DEBUG(("Added %p", server_entry));
1891 return server_entry;
1894 /* Removes server from the cache by the server entry. */
1896 SilcBool silc_client_del_server(SilcClient client, SilcClientConnection conn,
1897 SilcServerEntry server)
1904 if (silc_atomic_sub_int8(&server->internal.refcnt, 1) > 0)
1907 SILC_LOG_DEBUG(("Deleting server %p", server));
1909 silc_mutex_lock(conn->internal->lock);
1910 ret = silc_idcache_del_by_context(conn->internal->server_cache,
1912 silc_mutex_unlock(conn->internal->lock);
1914 silc_free(server->server_name);
1915 silc_free(server->server_info);
1916 if (server->public_key)
1917 silc_pkcs_public_key_free(server->public_key);
1918 silc_atomic_uninit8(&server->internal.refcnt);
1919 silc_rwlock_free(server->internal.lock);
1925 /* Updates the `server_entry' with the new information sent as argument. */
1927 void silc_client_update_server(SilcClient client,
1928 SilcClientConnection conn,
1929 SilcServerEntry server_entry,
1930 const char *server_name,
1931 const char *server_info)
1933 char *server_namec = NULL;
1935 SILC_LOG_DEBUG(("Updating server %p", server_entry));
1938 (!server_entry->server_name ||
1939 !silc_utf8_strcasecmp(server_entry->server_name, server_name))) {
1941 server_namec = silc_identifier_check(server_name, strlen(server_name),
1942 SILC_STRING_UTF8, 256, NULL);
1946 silc_free(server_entry->server_name);
1947 server_entry->server_name = strdup(server_name);
1948 if (!server_entry->server_name)
1951 /* Update cache entry */
1952 silc_mutex_lock(conn->internal->lock);
1953 silc_idcache_update_by_context(conn->internal->server_cache, server_entry,
1954 NULL, server_namec, TRUE);
1955 silc_mutex_unlock(conn->internal->lock);
1959 (!server_entry->server_info ||
1960 !silc_utf8_strcasecmp(server_entry->server_info, server_info))) {
1961 silc_free(server_entry->server_info);
1962 server_entry->server_info = strdup(server_info);
1968 void silc_client_lock_server(SilcServerEntry server_entry)
1970 silc_rwlock_rdlock(server_entry->internal.lock);
1975 void silc_client_unlock_server(SilcServerEntry server_entry)
1977 silc_rwlock_unlock(server_entry->internal.lock);
1980 /* Take reference of server entry */
1982 SilcServerEntry silc_client_ref_server(SilcClient client,
1983 SilcClientConnection conn,
1984 SilcServerEntry server_entry)
1986 silc_atomic_add_int8(&server_entry->internal.refcnt, 1);
1987 SILC_LOG_DEBUG(("Server %p refcnt %d->%d", server_entry,
1988 silc_atomic_get_int8(&server_entry->internal.refcnt) - 1,
1989 silc_atomic_get_int8(&server_entry->internal.refcnt)));
1990 return server_entry;
1993 /* Release reference of server entry */
1995 void silc_client_unref_server(SilcClient client, SilcClientConnection conn,
1996 SilcServerEntry server_entry)
1999 SILC_LOG_DEBUG(("Server %p refcnt %d->%d", server_entry,
2000 silc_atomic_get_int8(&server_entry->internal.refcnt),
2001 silc_atomic_get_int8(&server_entry->internal.refcnt)
2003 silc_client_del_server(client, conn, server_entry);
2007 /* Free server entry list */
2009 void silc_client_list_free_servers(SilcClient client,
2010 SilcClientConnection conn,
2011 SilcDList server_list)
2013 SilcServerEntry server_entry;
2016 silc_dlist_start(server_list);
2017 while ((server_entry = silc_dlist_get(server_list)))
2018 silc_client_unref_server(client, conn, server_entry);
2020 silc_dlist_uninit(server_list);