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 SilcClientEntry client_entry;
140 } *SilcClientGetClientInternal;
142 /* Resolving command callback */
144 static SilcBool silc_client_get_clients_cb(SilcClient client,
145 SilcClientConnection conn,
152 SilcClientGetClientInternal i = context;
153 SilcClientEntry client_entry;
155 if (error != SILC_STATUS_OK) {
156 SILC_LOG_DEBUG(("Resolving failed: %s", silc_get_status_message(error)));
158 if (i->client_entry) {
159 i->client_entry->internal.resolve_cmd_ident = 0;
160 silc_client_unref_client(client, conn, i->client_entry);
164 i->completion(client, conn, error, NULL, i->context);
168 /* Add the returned client to list */
170 client_entry = va_arg(ap, SilcClientEntry);
171 silc_client_ref_client(client, conn, client_entry);
172 silc_dlist_add(i->clients, client_entry);
173 client_entry->internal.resolve_cmd_ident = 0;
176 if (status == SILC_STATUS_OK || status == SILC_STATUS_LIST_END) {
177 /* Deliver the clients to the caller */
179 SILC_LOG_DEBUG(("Resolved %d clients", silc_dlist_count(i->clients)));
181 if (i->client_entry) {
182 i->client_entry->internal.resolve_cmd_ident = 0;
183 silc_client_unref_client(client, conn, i->client_entry);
186 silc_dlist_start(i->clients);
187 i->completion(client, conn, SILC_STATUS_OK, i->clients, i->context);
195 silc_client_list_free(client, conn, i->clients);
200 /* Resolves client information from server by the client ID. */
203 silc_client_get_client_by_id_resolve(SilcClient client,
204 SilcClientConnection conn,
205 SilcClientID *client_id,
206 SilcBuffer attributes,
207 SilcGetClientCallback completion,
210 SilcClientGetClientInternal i;
211 SilcClientEntry client_entry;
213 SilcUInt16 cmd_ident;
215 if (!client || !conn | !client_id)
218 SILC_LOG_DEBUG(("Resolve client by ID (%s)",
219 silc_id_render(client_id, SILC_ID_CLIENT)));
221 i = silc_calloc(1, sizeof(*i));
224 i->completion = completion;
225 i->context = context;
226 i->clients = silc_dlist_init();
232 /* Attach to resolving, if on going */
233 client_entry = silc_client_get_client_by_id(client, conn, client_id);
234 if (client_entry && client_entry->internal.resolve_cmd_ident) {
235 SILC_LOG_DEBUG(("Attach to existing resolving"));
236 silc_client_unref_client(client, conn, client_entry);
237 silc_client_command_pending(conn, SILC_COMMAND_NONE,
238 client_entry->internal.resolve_cmd_ident,
239 silc_client_get_clients_cb, i);
240 return client_entry->internal.resolve_cmd_ident;
243 /* Send the command */
244 idp = silc_id_payload_encode(client_id, SILC_ID_CLIENT);
245 cmd_ident = silc_client_command_send(client, conn, SILC_COMMAND_WHOIS,
246 silc_client_get_clients_cb, i,
247 2, 3, silc_buffer_datalen(attributes),
248 4, silc_buffer_datalen(idp));
249 if (!cmd_ident && completion)
250 completion(client, conn, SILC_STATUS_ERR_RESOURCE_LIMIT, NULL, context);
252 if (client_entry && cmd_ident) {
253 client_entry->internal.resolve_cmd_ident = cmd_ident;
254 i->client_entry = client_entry;
256 silc_client_unref_client(client, conn, client_entry);
259 silc_buffer_free(idp);
264 /* Finds client entry or entries by the `nickname' and `server'. The
265 completion callback will be called when the client entries has been
266 found. Used internally by the library. */
268 static SilcUInt16 silc_client_get_clients_i(SilcClient client,
269 SilcClientConnection conn,
271 const char *nickname,
273 SilcBuffer attributes,
274 SilcGetClientCallback completion,
277 SilcClientGetClientInternal i;
278 char userhost[768 + 1];
281 SILC_LOG_DEBUG(("Resolve client by %s command",
282 silc_get_command_name(command)));
284 if (!client || !conn)
286 if (!nickname && !attributes)
289 i = silc_calloc(1, sizeof(*i));
292 i->clients = silc_dlist_init();
297 i->completion = completion;
298 i->context = context;
300 memset(userhost, 0, sizeof(userhost));
301 if (nickname && server) {
302 len = strlen(nickname) + strlen(server) + 3;
303 silc_strncat(userhost, len, nickname, strlen(nickname));
304 silc_strncat(userhost, len, "@", 1);
305 silc_strncat(userhost, len, server, strlen(server));
306 } else if (nickname) {
307 silc_strncat(userhost, sizeof(userhost) - 1, nickname, strlen(nickname));
310 /* Send the command */
311 if (command == SILC_COMMAND_IDENTIFY)
312 return silc_client_command_send(client, conn, SILC_COMMAND_IDENTIFY,
313 silc_client_get_clients_cb, i,
314 1, 1, userhost, strlen(userhost));
315 return silc_client_command_send(client, conn, SILC_COMMAND_WHOIS,
316 silc_client_get_clients_cb, i,
317 2, 1, userhost, strlen(userhost),
318 3, silc_buffer_datalen(attributes));
321 /* Get clients from server with IDENTIFY command */
323 SilcUInt16 silc_client_get_clients(SilcClient client,
324 SilcClientConnection conn,
325 const char *nickname,
327 SilcGetClientCallback completion,
330 return silc_client_get_clients_i(client, conn, SILC_COMMAND_IDENTIFY,
331 nickname, server, NULL,
332 completion, context);
335 /* Get clients from server with WHOIS command */
337 SilcUInt16 silc_client_get_clients_whois(SilcClient client,
338 SilcClientConnection conn,
339 const char *nickname,
341 SilcBuffer attributes,
342 SilcGetClientCallback completion,
345 return silc_client_get_clients_i(client, conn, SILC_COMMAND_WHOIS,
346 nickname, server, attributes,
347 completion, context);
350 /* ID list resolving context */
352 SilcGetClientCallback completion;
354 SilcBuffer client_id_list;
355 SilcUInt32 list_count;
356 } *GetClientsByListInternal;
358 static SilcBool silc_client_get_clients_list_cb(SilcClient client,
359 SilcClientConnection conn,
366 GetClientsByListInternal i = context;
367 SilcClientEntry client_entry;
373 /* Process the list after all replies have been received */
374 if (status != SILC_STATUS_OK && !SILC_STATUS_IS_ERROR(status) &&
375 status != SILC_STATUS_LIST_END)
378 SILC_LOG_DEBUG(("Resolved all clients"));
380 clients = silc_dlist_init();
382 status = SILC_STATUS_ERR_RESOURCE_LIMIT;
386 for (c = 0; c < i->list_count; c++) {
388 SILC_GET16_MSB(idp_len, i->client_id_list->data + 2);
390 if (!silc_id_payload_parse_id(i->client_id_list->data, idp_len, &id)) {
391 status = SILC_STATUS_ERR_BAD_CLIENT_ID;
395 /* Get client entry */
396 client_entry = silc_client_get_client_by_id(client, conn, &id.u.client_id);
398 silc_dlist_add(clients, client_entry);
400 if (!silc_buffer_pull(i->client_id_list, idp_len)) {
401 status = SILC_STATUS_ERR_BAD_CLIENT_ID;
406 silc_dlist_start(clients);
407 status = SILC_STATUS_OK;
409 i->completion(client, conn, status, clients, i->context);
412 if (status != SILC_STATUS_OK && i->completion)
413 i->completion(client, conn, status, NULL, i->context);
415 silc_client_list_free(client, conn, clients);
416 silc_buffer_free(i->client_id_list);
422 /* Gets client entries by the list of client ID's `client_id_list'. This
423 always resolves those client ID's it does not know yet from the server
424 so this function might take a while. The `client_id_list' is a list
425 of ID Payloads added one after other. JOIN command reply and USERS
426 command reply for example returns this sort of list. The `completion'
427 will be called after the entries are available. */
429 SilcUInt16 silc_client_get_clients_by_list(SilcClient client,
430 SilcClientConnection conn,
431 SilcUInt32 list_count,
432 SilcBuffer client_id_list,
433 SilcGetClientCallback completion,
436 GetClientsByListInternal in;
437 SilcClientEntry entry;
438 unsigned char **res_argv = NULL;
439 SilcUInt32 *res_argv_lens = NULL, *res_argv_types = NULL, res_argc = 0;
440 SilcUInt16 idp_len, cmd_ident;
445 SILC_LOG_DEBUG(("Resolve clients from Client ID list"));
447 if (!client || !conn || !client_id_list)
450 in = silc_calloc(1, sizeof(*in));
453 in->completion = completion;
454 in->context = context;
455 in->list_count = list_count;
456 in->client_id_list = silc_buffer_copy(client_id_list);
457 if (!in->client_id_list)
460 for (i = 0; i < list_count; i++) {
462 SILC_GET16_MSB(idp_len, client_id_list->data + 2);
464 if (!silc_id_payload_parse_id(client_id_list->data, idp_len, &id))
467 /* Check if we have this client cached already. If we don't have the
468 entry or it has incomplete info, then resolve it from the server. */
469 entry = silc_client_get_client_by_id(client, conn, &id.u.client_id);
470 if (!entry || !entry->nickname[0] || !entry->username[0] ||
473 res_argv = silc_calloc(list_count, sizeof(*res_argv));
474 res_argv_lens = silc_calloc(list_count, sizeof(*res_argv_lens));
475 res_argv_types = silc_calloc(list_count, sizeof(*res_argv_types));
476 if (!res_argv || !res_argv_lens || !res_argv_types) {
477 silc_client_unref_client(client, conn, entry);
482 res_argv[res_argc] = client_id_list->data;
483 res_argv_lens[res_argc] = idp_len;
484 res_argv_types[res_argc] = res_argc + 4;
487 silc_client_unref_client(client, conn, entry);
489 if (!silc_buffer_pull(client_id_list, idp_len))
492 silc_buffer_start(client_id_list);
494 /* Query the unknown client information from server */
496 cmd_ident = silc_client_command_send_argv(client,
497 conn, SILC_COMMAND_WHOIS,
498 silc_client_get_clients_list_cb,
499 in, res_argc, res_argv,
503 silc_free(res_argv_lens);
504 silc_free(res_argv_types);
508 /* We have the clients in cache, get them and call the completion */
509 silc_client_get_clients_list_cb(client, conn, SILC_COMMAND_WHOIS,
510 SILC_STATUS_OK, SILC_STATUS_OK, in, tmp);
514 silc_buffer_free(in->client_id_list);
517 silc_free(res_argv_lens);
518 silc_free(res_argv_types);
525 SilcClientConnection conn;
526 SilcChannelID channel_id;
527 SilcGetClientCallback completion;
530 } *GetClientsByChannelInternal;
532 SILC_CLIENT_CMD_FUNC(get_clients_by_channel_cb)
534 GetClientsByChannelInternal i = context;
535 SilcClientEntry *clients = NULL;
536 SilcUInt32 clients_count = 0;
537 SilcBool found = FALSE;
538 SilcChannelEntry channel;
539 SilcHashTableList htl;
548 channel = silc_client_get_channel_by_id(i->client, i->conn, &i->channel_id);
549 if (channel && !silc_hash_table_count(channel->user_list)) {
550 clients = silc_calloc(silc_hash_table_count(channel->user_list),
552 silc_hash_table_list(channel->user_list, &htl);
553 while (silc_hash_table_get(&htl, NULL, (void *)&chu))
554 clients[clients_count++] = chu->client;
555 silc_hash_table_list_reset(&htl);
560 i->completion(i->client, i->conn, clients, clients_count, i->context);
563 i->completion(i->client, i->conn, NULL, 0, i->context);
569 /* Gets client entries by the channel entry indicated by `channel'. Thus,
570 it resolves the clients currently on that channel. */
572 void silc_client_get_clients_by_channel(SilcClient client,
573 SilcClientConnection conn,
574 SilcChannelEntry channel,
575 SilcGetClientCallback completion,
578 GetClientsByChannelInternal in;
579 SilcHashTableList htl;
581 SilcClientEntry entry;
582 unsigned char **res_argv = NULL;
583 SilcUInt32 *res_argv_lens = NULL, *res_argv_types = NULL, res_argc = 0;
585 SilcBool wait_res = FALSE;
587 assert(client && conn && channel);
589 SILC_LOG_DEBUG(("Start"));
591 in = silc_calloc(1, sizeof(*in));
594 in->channel_id = *channel->id;
595 in->completion = completion;
596 in->context = context;
598 /* If user list does not exist, send USERS command. */
599 if (!channel->user_list || !silc_hash_table_count(channel->user_list)) {
600 SILC_LOG_DEBUG(("Sending USERS"));
601 silc_client_command_register(client, SILC_COMMAND_USERS, NULL, NULL,
602 silc_client_command_reply_users_i, 0,
604 silc_client_command_send(client, conn, SILC_COMMAND_USERS,
605 conn->cmd_ident, 1, 2, channel->channel_name,
606 strlen(channel->channel_name));
607 silc_client_command_pending(conn, SILC_COMMAND_USERS, conn->cmd_ident,
608 silc_client_command_get_clients_by_channel_cb,
613 silc_hash_table_list(channel->user_list, &htl);
614 while (silc_hash_table_get(&htl, NULL, (void *)&chu)) {
617 /* If the entry has incomplete info, then resolve it from the server. */
618 if (!entry->nickname[0] || !entry->realname) {
619 if (entry->status & SILC_CLIENT_STATUS_RESOLVING) {
620 /* Attach to this resolving and wait until it finishes */
621 silc_client_command_pending(
622 conn, SILC_COMMAND_NONE,
623 entry->resolve_cmd_ident,
624 silc_client_command_get_clients_by_channel_cb,
630 entry->status |= SILC_CLIENT_STATUS_RESOLVING;
631 entry->resolve_cmd_ident = conn->cmd_ident + 1;
633 idp = silc_id_payload_encode(entry->id, SILC_ID_CLIENT);
635 /* No we don't have it, query it from the server. Assemble argument
636 table that will be sent for the WHOIS command later. */
637 res_argv = silc_realloc(res_argv, sizeof(*res_argv) *
639 res_argv_lens = silc_realloc(res_argv_lens, sizeof(*res_argv_lens) *
641 res_argv_types = silc_realloc(res_argv_types, sizeof(*res_argv_types) *
643 res_argv[res_argc] = silc_memdup(idp->data, idp->len);
644 res_argv_lens[res_argc] = idp->len;
645 res_argv_types[res_argc] = res_argc + 4;
648 silc_buffer_free(idp);
651 silc_hash_table_list_reset(&htl);
653 /* Query the client information from server if the list included clients
654 that we don't know about. */
658 /* Send the WHOIS command to server */
659 res_cmd = silc_command_payload_encode(SILC_COMMAND_WHOIS,
660 res_argc, res_argv, res_argv_lens,
661 res_argv_types, ++conn->cmd_ident);
662 silc_client_packet_send(client, conn->sock, SILC_PACKET_COMMAND,
663 NULL, 0, NULL, NULL, res_cmd->data, res_cmd->len,
666 /* Register our own command reply for this command */
667 silc_client_command_register(client, SILC_COMMAND_WHOIS, NULL, NULL,
668 silc_client_command_reply_whois_i, 0,
671 /* Process the applications request after reply has been received */
672 silc_client_command_pending(
673 conn, SILC_COMMAND_WHOIS, conn->cmd_ident,
674 silc_client_command_get_clients_by_channel_cb,
678 silc_buffer_free(res_cmd);
680 silc_free(res_argv_lens);
681 silc_free(res_argv_types);
688 /* We have the clients in cache, get them and call the completion */
689 silc_client_command_get_clients_by_channel_cb((void *)in, NULL);
694 /************************** Client Entry Routines ***************************/
696 /* Creates new client entry and adds it to the ID cache. Returns pointer
699 SilcClientEntry silc_client_add_client(SilcClient client,
700 SilcClientConnection conn,
701 char *nickname, char *username,
702 char *userinfo, SilcClientID *id,
705 SilcClientEntry client_entry;
708 SILC_LOG_DEBUG(("Adding new client entry"));
710 /* Save the client infos */
711 client_entry = silc_calloc(1, sizeof(*client_entry));
715 silc_rwlock_alloc(&client_entry->internal.lock);
716 silc_atomic_init8(&client_entry->internal.refcnt, 0);
717 client_entry->id = *id;
718 client_entry->mode = mode;
719 client_entry->realname = userinfo ? strdup(userinfo) : NULL;
720 silc_parse_userfqdn(nickname, client_entry->nickname,
721 sizeof(client_entry->nickname),
722 client_entry->server,
723 sizeof(client_entry->server));
724 silc_parse_userfqdn(username, client_entry->username,
725 sizeof(client_entry->username),
726 client_entry->hostname,
727 sizeof(client_entry->hostname));
728 client_entry->channels = silc_hash_table_alloc(1, silc_hash_ptr, NULL, NULL,
729 NULL, NULL, NULL, TRUE);
730 if (!client_entry->channels) {
731 silc_free(client_entry->realname);
732 silc_free(client_entry);
736 /* Normalize nickname */
737 if (client_entry->nickname[0]) {
738 nick = silc_identifier_check(client_entry->nickname,
739 strlen(client_entry->nickname),
740 SILC_STRING_UTF8, 128, NULL);
742 silc_free(client_entry->realname);
743 silc_hash_table_free(client_entry->channels);
744 silc_free(client_entry);
749 silc_mutex_lock(conn->internal->lock);
751 /* Add client to cache, the normalized nickname is saved to cache */
752 if (!silc_idcache_add(conn->internal->client_cache, nick,
753 &client_entry->id, client_entry)) {
755 silc_free(client_entry->realname);
756 silc_hash_table_free(client_entry->channels);
757 silc_free(client_entry);
758 silc_mutex_unlock(conn->internal->lock);
762 client_entry->nickname_normalized = nick;
764 silc_mutex_unlock(conn->internal->lock);
765 silc_client_ref_client(client, conn, client_entry);
767 /* Format the nickname */
768 silc_client_nickname_format(client, conn, client_entry, FALSE);
770 if (client_entry->nickname[0])
771 client_entry->internal.valid = TRUE;
773 SILC_LOG_DEBUG(("Added %p", client_entry));
778 /* Updates the `client_entry' with the new information sent as argument.
779 This handles entry locking internally. */
781 void silc_client_update_client(SilcClient client,
782 SilcClientConnection conn,
783 SilcClientEntry client_entry,
784 const char *nickname,
785 const char *username,
786 const char *userinfo,
791 SILC_LOG_DEBUG(("Update client entry"));
793 silc_rwlock_wrlock(client_entry->internal.lock);
795 if (!client_entry->realname && userinfo)
796 client_entry->realname = strdup(userinfo);
797 if ((!client_entry->username[0] || !client_entry->hostname[0]) && username)
798 silc_parse_userfqdn(username, client_entry->username,
799 sizeof(client_entry->username),
800 client_entry->hostname,
801 sizeof(client_entry->username));
802 if (!client_entry->nickname[0] && nickname) {
803 silc_parse_userfqdn(nickname, client_entry->nickname,
804 sizeof(client_entry->nickname),
805 client_entry->server,
806 sizeof(client_entry->server));
808 /* Normalize nickname */
809 nick = silc_identifier_check(client_entry->nickname,
810 strlen(client_entry->nickname),
811 SILC_STRING_UTF8, 128, NULL);
813 silc_rwlock_unlock(client_entry->internal.lock);
817 /* Format nickname */
818 silc_client_nickname_format(client, conn, client_entry,
819 client_entry == conn->local_entry);
821 /* Update cache entry */
822 silc_mutex_lock(conn->internal->lock);
823 silc_idcache_update_by_context(conn->internal->client_cache,
824 client_entry, NULL, nick, TRUE);
825 silc_mutex_unlock(conn->internal->lock);
826 client_entry->nickname_normalized = nick;
827 client_entry->internal.valid = TRUE;
829 client_entry->mode = mode;
831 silc_rwlock_unlock(client_entry->internal.lock);
834 /* Change a client's nickname. Must be called with `client_entry' locked. */
836 SilcBool silc_client_change_nickname(SilcClient client,
837 SilcClientConnection conn,
838 SilcClientEntry client_entry,
839 const char *new_nick,
840 SilcClientID *new_id,
841 const unsigned char *idp,
846 SILC_LOG_DEBUG(("Change nickname %s to %s", client_entry->nickname,
849 /* Normalize nickname */
850 tmp = silc_identifier_check(new_nick, strlen(new_nick),
851 SILC_STRING_UTF8, 128, NULL);
855 /* Update the client entry */
856 silc_mutex_lock(conn->internal->lock);
857 if (!silc_idcache_update_by_context(conn->internal->client_cache,
858 client_entry, new_id, tmp, TRUE)) {
860 silc_mutex_unlock(conn->internal->lock);
863 silc_mutex_unlock(conn->internal->lock);
865 memset(client_entry->nickname, 0, sizeof(client_entry->nickname));
866 memcpy(client_entry->nickname, new_nick, strlen(new_nick));
867 client_entry->nickname_normalized = tmp;
868 silc_client_nickname_format(client, conn, client_entry,
869 client_entry == conn->local_entry);
871 /* For my client entry, update ID and set new ID to packet stream */
872 if (client_entry == conn->local_entry) {
873 if (idp && idp_len) {
874 silc_buffer_enlarge(conn->internal->local_idp, idp_len);
875 silc_buffer_put(conn->internal->local_idp, idp, idp_len);
878 silc_packet_set_ids(conn->stream, SILC_ID_CLIENT, conn->local_id,
882 client_entry->internal.valid = TRUE;
886 /* Deletes the client entry and frees all memory. */
888 void silc_client_del_client_entry(SilcClient client,
889 SilcClientConnection conn,
890 SilcClientEntry client_entry)
892 silc_free(client_entry->realname);
893 silc_free(client_entry->nickname_normalized);
894 silc_free(client_entry->internal.key);
895 if (client_entry->public_key)
896 silc_pkcs_public_key_free(client_entry->public_key);
897 silc_hash_table_free(client_entry->channels);
898 if (client_entry->internal.send_key)
899 silc_cipher_free(client_entry->internal.send_key);
900 if (client_entry->internal.receive_key)
901 silc_cipher_free(client_entry->internal.receive_key);
902 if (client_entry->internal.hmac_send)
903 silc_hmac_free(client_entry->internal.hmac_send);
904 if (client_entry->internal.hmac_receive)
905 silc_hmac_free(client_entry->internal.hmac_receive);
907 silc_client_ftp_session_free_client(conn, client_entry);
908 if (client_entry->internal->ke)
909 silc_client_abort_key_agreement(client, conn, client_entry);
911 silc_atomic_uninit8(&client_entry->internal.refcnt);
912 silc_rwlock_free(client_entry->internal.lock);
913 silc_free(client_entry);
916 /* Removes client from the cache by the client entry. */
918 SilcBool silc_client_del_client(SilcClient client, SilcClientConnection conn,
919 SilcClientEntry client_entry)
926 if (silc_atomic_sub_int8(&client_entry->internal.refcnt, 1) > 0)
929 SILC_LOG_DEBUG(("Deleting client %p", client_entry));
931 silc_mutex_lock(conn->internal->lock);
932 ret = silc_idcache_del_by_context(conn->internal->client_cache,
934 silc_mutex_unlock(conn->internal->lock);
937 /* Remove from channels */
938 silc_client_remove_from_channels(client, conn, client_entry);
940 /* Free the client entry data */
941 silc_client_del_client_entry(client, conn, client_entry);
947 /* Internal routine used to find client by ID and if not found this creates
948 new client entry and returns it. */
950 SilcClientEntry silc_client_get_client(SilcClient client,
951 SilcClientConnection conn,
952 SilcClientID *client_id)
954 SilcClientEntry client_entry;
956 client_entry = silc_client_get_client_by_id(client, conn, client_id);
958 client_entry = silc_client_add_client(client, conn, NULL, NULL, NULL,
962 silc_client_ref_client(client, conn, client_entry);
970 void silc_client_lock_client(SilcClientEntry client_entry)
972 silc_rwlock_rdlock(client_entry->internal.lock);
977 void silc_client_unlock_client(SilcClientEntry client_entry)
979 silc_rwlock_unlock(client_entry->internal.lock);
982 /* Take reference of client entry */
984 SilcClientEntry silc_client_ref_client(SilcClient client,
985 SilcClientConnection conn,
986 SilcClientEntry client_entry)
988 silc_atomic_add_int8(&client_entry->internal.refcnt, 1);
989 SILC_LOG_DEBUG(("Client %p refcnt %d->%d", client_entry,
990 silc_atomic_get_int8(&client_entry->internal.refcnt) - 1,
991 silc_atomic_get_int8(&client_entry->internal.refcnt)));
995 /* Release reference of client entry */
997 void silc_client_unref_client(SilcClient client, SilcClientConnection conn,
998 SilcClientEntry client_entry)
1001 SILC_LOG_DEBUG(("Client %p refcnt %d->%d", client_entry,
1002 silc_atomic_get_int8(&client_entry->internal.refcnt),
1003 silc_atomic_get_int8(&client_entry->internal.refcnt) - 1));
1004 silc_client_del_client(client, conn, client_entry);
1008 /* Free client entry list */
1010 void silc_client_list_free(SilcClient client, SilcClientConnection conn,
1011 SilcDList client_list)
1013 SilcClientEntry client_entry;
1016 silc_dlist_start(client_list);
1017 while ((client_entry = silc_dlist_get(client_list)))
1018 silc_client_unref_client(client, conn, client_entry);
1020 silc_dlist_uninit(client_list);
1024 /* Formats the nickname of the client specified by the `client_entry'.
1025 If the format is specified by the application this will format the
1026 nickname and replace the old nickname in the client entry. If the
1027 format string is not specified then this function has no effect.
1028 Returns the client entry that was formatted. */
1030 SilcClientEntry silc_client_nickname_format(SilcClient client,
1031 SilcClientConnection conn,
1032 SilcClientEntry client_entry,
1036 char newnick[128 + 1];
1037 int i, off = 0, len;
1040 SilcClientEntry entry, unformatted = NULL;
1042 if (!client->internal->params->nickname_format[0])
1043 return client_entry;
1044 if (!client_entry->nickname[0])
1047 SILC_LOG_DEBUG(("Format nickname"));
1049 /* Get all clients with same nickname. Do not perform the formatting
1050 if there aren't any clients with same nickname unless the application
1051 is forcing us to do so. */
1052 clients = silc_client_get_clients_local(client, conn,
1053 client_entry->nickname, NULL);
1056 if (silc_dlist_count(clients) == 1 &&
1057 !client->internal->params->nickname_force_format) {
1058 silc_client_list_free(client, conn, clients);
1059 return client_entry;
1064 while ((entry = silc_dlist_get(clients))) {
1065 if (entry->internal.valid && entry != client_entry)
1067 if (entry->internal.valid && entry != client_entry &&
1068 silc_utf8_strcasecmp(entry->nickname, client_entry->nickname)) {
1070 unformatted = entry;
1074 if (!len || freebase) {
1075 silc_client_list_free(client, conn, clients);
1076 return client_entry;
1079 /* If priority formatting, this client always gets unformatted nickname. */
1080 if (unformatted && priority)
1081 client_entry = unformatted;
1083 memset(newnick, 0, sizeof(newnick));
1084 cp = client->internal->params->nickname_format;
1094 if (!client_entry->nickname[0])
1096 len = strlen(client_entry->nickname);
1097 memcpy(&newnick[off], client_entry->nickname, len);
1101 /* Stripped hostname */
1102 if (!client_entry->hostname[0])
1104 len = strcspn(client_entry->hostname, ".");
1105 i = strcspn(client_entry->hostname, "-");
1108 memcpy(&newnick[off], client_entry->hostname, len);
1113 if (!client_entry->hostname[0])
1115 len = strlen(client_entry->hostname);
1116 memcpy(&newnick[off], client_entry->hostname, len);
1120 /* Stripped server name */
1121 if (!client_entry->server)
1123 len = strcspn(client_entry->server, ".");
1124 memcpy(&newnick[off], client_entry->server, len);
1128 /* Full server name */
1129 if (!client_entry->server)
1131 len = strlen(client_entry->server);
1132 memcpy(&newnick[off], client_entry->server, len);
1136 /* Ascending number */
1141 if (silc_dlist_count(clients) == 1)
1144 silc_dlist_start(clients);
1145 while ((entry = silc_dlist_get(clients))) {
1146 if (!silc_utf8_strncasecmp(entry->nickname, newnick, off))
1148 if (strlen(entry->nickname) <= off)
1150 num = atoi(&entry->nickname[off]);
1155 memset(tmp, 0, sizeof(tmp));
1156 silc_snprintf(tmp, sizeof(tmp) - 1, "%d", ++max);
1158 memcpy(&newnick[off], tmp, len);
1163 /* Some other character in the string */
1164 memcpy(&newnick[off], cp, 1);
1173 memcpy(client_entry->nickname, newnick, strlen(newnick));
1174 silc_client_list_free(client, conn, clients);
1176 return client_entry;
1179 /* Parses nickname according to nickname format string */
1181 SilcBool silc_client_nickname_parse(SilcClient client,
1182 SilcClientConnection conn,
1186 char *cp, s = 0, e = 0, *nick;
1190 if (!client->internal->params->nickname_format[0])
1193 if (!nickname || !nickname[0])
1196 cp = client->internal->params->nickname_format;
1216 /* Get separator character */
1229 /* Parse the nickname */
1233 if (strchr(nickname, s))
1234 nick = strchr(nickname, s) + 1;
1236 if (strchr(nick, e))
1237 len = strchr(nick, e) - nick;
1241 *ret_nick = silc_memdup(nick, len);
1245 SILC_LOG_DEBUG(("Parsed nickname: %s", *ret_nick));
1250 /************************ Channel Searching Locally *************************/
1252 /* Finds entry for channel by the channel name. Returns the entry or NULL
1253 if the entry was not found. It is found only if the client is joined
1256 SilcChannelEntry silc_client_get_channel(SilcClient client,
1257 SilcClientConnection conn,
1260 SilcIDCacheEntry id_cache;
1261 SilcChannelEntry entry;
1263 if (!client || !conn || !channel)
1266 SILC_LOG_DEBUG(("Find channel %s", channel));
1268 /* Normalize name for search */
1269 channel = silc_channel_name_check(channel, strlen(channel), SILC_STRING_UTF8,
1274 silc_mutex_lock(conn->internal->lock);
1276 if (!silc_idcache_find_by_name_one(conn->internal->channel_cache, channel,
1278 silc_mutex_unlock(conn->internal->lock);
1283 SILC_LOG_DEBUG(("Found"));
1285 entry = id_cache->context;
1288 silc_client_ref_channel(client, conn, entry);
1289 silc_mutex_unlock(conn->internal->lock);
1296 /* Finds entry for channel by the channel ID. Returns the entry or NULL
1297 if the entry was not found. It is found only if the client is joined
1300 SilcChannelEntry silc_client_get_channel_by_id(SilcClient client,
1301 SilcClientConnection conn,
1302 SilcChannelID *channel_id)
1304 SilcIDCacheEntry id_cache;
1305 SilcChannelEntry entry;
1307 if (!client || !conn || !channel_id)
1310 SILC_LOG_DEBUG(("Find channel by id %s",
1311 silc_id_render(channel_id, SILC_ID_CHANNEL)));
1313 silc_mutex_lock(conn->internal->lock);
1315 if (!silc_idcache_find_by_id_one(conn->internal->channel_cache, channel_id,
1317 silc_mutex_unlock(conn->internal->lock);
1321 SILC_LOG_DEBUG(("Found"));
1323 entry = id_cache->context;
1326 silc_client_ref_channel(client, conn, entry);
1327 silc_mutex_unlock(conn->internal->lock);
1332 /********************** Channel Resolving from Server ***********************/
1334 /* Channel resolving context */
1337 SilcGetChannelCallback completion;
1339 } *SilcClientGetChannelInternal;
1341 /* Resolving command callback */
1343 static SilcBool silc_client_get_channel_cb(SilcClient client,
1344 SilcClientConnection conn,
1345 SilcCommand command,
1351 SilcClientGetChannelInternal i = context;
1352 SilcChannelEntry entry;
1354 if (error != SILC_STATUS_OK) {
1355 SILC_LOG_DEBUG(("Resolving failed: %s", silc_get_status_message(error)));
1357 i->completion(client, conn, error, NULL, i->context);
1361 /* Add the returned channel to list */
1362 if (i->completion) {
1363 entry = va_arg(ap, SilcChannelEntry);
1364 silc_client_ref_channel(client, conn, entry);
1365 silc_dlist_add(i->channels, entry);
1368 if (status == SILC_STATUS_OK || status == SILC_STATUS_LIST_END) {
1369 /* Deliver the channels to the caller */
1370 if (i->completion) {
1371 SILC_LOG_DEBUG(("Resolved %d channels", silc_dlist_count(i->channels)));
1372 silc_dlist_start(i->channels);
1373 i->completion(client, conn, SILC_STATUS_OK, i->channels, i->context);
1381 silc_client_list_free_channels(client, conn, i->channels);
1386 /* Resolves channel entry from the server by the channel name. */
1388 void silc_client_get_channel_resolve(SilcClient client,
1389 SilcClientConnection conn,
1391 SilcGetChannelCallback completion,
1394 SilcClientGetChannelInternal i;
1396 if (!client || !conn || !channel_name || !completion)
1399 SILC_LOG_DEBUG(("Resolve channel %s", channel_name));
1401 i = silc_calloc(1, sizeof(*i));
1404 i->completion = completion;
1405 i->context = context;
1406 i->channels = silc_dlist_init();
1412 /* Send the command */
1413 if (!silc_client_command_send(client, conn, SILC_COMMAND_IDENTIFY,
1414 silc_client_get_channel_cb, i, 1,
1415 3, channel_name, strlen(channel_name))) {
1417 completion(client, conn, SILC_STATUS_ERR_RESOURCE_LIMIT, NULL, context);
1421 /* Resolves channel information from the server by the channel ID. */
1424 silc_client_get_channel_by_id_resolve(SilcClient client,
1425 SilcClientConnection conn,
1426 SilcChannelID *channel_id,
1427 SilcGetChannelCallback completion,
1430 SilcClientGetChannelInternal i;
1432 SilcUInt16 cmd_ident;
1434 if (!client || !conn || !channel_id || !completion)
1437 SILC_LOG_DEBUG(("Resolve channel by id %s",
1438 silc_id_render(channel_id, SILC_ID_CHANNEL)));
1440 i = silc_calloc(1, sizeof(*i));
1443 i->completion = completion;
1444 i->context = context;
1445 i->channels = silc_dlist_init();
1451 /* Send the command */
1452 idp = silc_id_payload_encode(channel_id, SILC_ID_CHANNEL);
1453 cmd_ident = silc_client_command_send(client, conn, SILC_COMMAND_IDENTIFY,
1454 silc_client_get_channel_cb, i, 1,
1455 5, silc_buffer_datalen(idp));
1456 silc_buffer_free(idp);
1457 if (!cmd_ident && completion)
1458 completion(client, conn, SILC_STATUS_ERR_RESOURCE_LIMIT, NULL, context);
1463 /************************* Channel Entry Routines ***************************/
1465 /* Add new channel entry to the ID Cache */
1467 SilcChannelEntry silc_client_add_channel(SilcClient client,
1468 SilcClientConnection conn,
1469 const char *channel_name,
1471 SilcChannelID *channel_id)
1473 SilcChannelEntry channel;
1474 char *channel_namec;
1476 SILC_LOG_DEBUG(("Start"));
1478 channel = silc_calloc(1, sizeof(*channel));
1482 silc_rwlock_alloc(&channel->internal.lock);
1483 silc_atomic_init16(&channel->internal.refcnt, 0);
1484 channel->id = *channel_id;
1485 channel->mode = mode;
1487 channel->channel_name = strdup(channel_name);
1488 if (!channel->channel_name) {
1493 channel->user_list = silc_hash_table_alloc(1, silc_hash_ptr, NULL, NULL,
1494 NULL, NULL, NULL, TRUE);
1495 if (!channel->user_list) {
1496 silc_free(channel->channel_name);
1501 /* Normalize channel name */
1502 channel_namec = silc_channel_name_check(channel_name, strlen(channel_name),
1503 SILC_STRING_UTF8, 256, NULL);
1504 if (!channel_namec) {
1505 silc_free(channel->channel_name);
1506 silc_hash_table_free(channel->user_list);
1511 silc_mutex_lock(conn->internal->lock);
1513 /* Add channel to cache, the normalized channel name is saved to cache */
1514 if (!silc_idcache_add(conn->internal->channel_cache, channel_namec,
1515 &channel->id, channel)) {
1516 silc_free(channel_namec);
1517 silc_free(channel->channel_name);
1518 silc_hash_table_free(channel->user_list);
1520 silc_mutex_unlock(conn->internal->lock);
1524 silc_mutex_unlock(conn->internal->lock);
1525 silc_client_ref_channel(client, conn, channel);
1527 SILC_LOG_DEBUG(("Added %p", channel));
1532 /* Removes channel from the cache by the channel entry. */
1534 SilcBool silc_client_del_channel(SilcClient client, SilcClientConnection conn,
1535 SilcChannelEntry channel)
1544 if (silc_atomic_sub_int16(&channel->internal.refcnt, 1) > 0)
1547 SILC_LOG_DEBUG(("Deleting channel %p", channel));
1549 silc_mutex_lock(conn->internal->lock);
1550 ret = silc_idcache_del_by_context(conn->internal->channel_cache,
1552 silc_mutex_unlock(conn->internal->lock);
1557 silc_client_empty_channel(client, conn, channel);
1558 silc_hash_table_free(channel->user_list);
1559 silc_free(channel->channel_name);
1560 silc_free(channel->topic);
1561 if (channel->founder_key)
1562 silc_pkcs_public_key_free(channel->founder_key);
1563 if (channel->internal.send_key)
1564 silc_cipher_free(channel->internal.send_key);
1565 if (channel->internal.receive_key)
1566 silc_cipher_free(channel->internal.receive_key);
1567 if (channel->internal.hmac)
1568 silc_hmac_free(channel->internal.hmac);
1569 if (channel->internal.old_channel_keys) {
1570 silc_dlist_start(channel->internal.old_channel_keys);
1571 while ((key = silc_dlist_get(channel->internal.old_channel_keys)))
1572 silc_cipher_free(key);
1573 silc_dlist_uninit(channel->internal.old_channel_keys);
1575 if (channel->internal.old_hmacs) {
1576 silc_dlist_start(channel->internal.old_hmacs);
1577 while ((hmac = silc_dlist_get(channel->internal.old_hmacs)))
1578 silc_hmac_free(hmac);
1579 silc_dlist_uninit(channel->internal.old_hmacs);
1581 if (channel->channel_pubkeys)
1582 silc_argument_list_free(channel->channel_pubkeys,
1583 SILC_ARGUMENT_PUBLIC_KEY);
1584 silc_client_del_channel_private_keys(client, conn, channel);
1585 silc_atomic_uninit16(&channel->internal.refcnt);
1586 silc_rwlock_free(channel->internal.lock);
1587 silc_schedule_task_del_by_context(conn->client->schedule, channel);
1593 /* Replaces the channel ID of the `channel' to `new_id'. Returns FALSE
1594 if the ID could not be changed. This handles entry locking internally. */
1596 SilcBool silc_client_replace_channel_id(SilcClient client,
1597 SilcClientConnection conn,
1598 SilcChannelEntry channel,
1599 SilcChannelID *new_id)
1601 SilcBool ret = FALSE;
1606 SILC_LOG_DEBUG(("Old Channel ID id(%s)",
1607 silc_id_render(&channel->id, SILC_ID_CHANNEL)));
1608 SILC_LOG_DEBUG(("New Channel ID id(%s)",
1609 silc_id_render(new_id, SILC_ID_CHANNEL)));
1612 silc_rwlock_wrlock(channel->internal.lock);
1613 silc_mutex_lock(conn->internal->lock);
1614 silc_idcache_update_by_context(conn->internal->channel_cache, channel,
1615 new_id, NULL, FALSE);
1616 silc_mutex_unlock(conn->internal->lock);
1617 silc_rwlock_unlock(channel->internal.lock);
1624 void silc_client_lock_channel(SilcChannelEntry channel_entry)
1626 silc_rwlock_rdlock(channel_entry->internal.lock);
1631 void silc_client_unlock_channel(SilcChannelEntry channel_entry)
1633 silc_rwlock_unlock(channel_entry->internal.lock);
1636 /* Take reference of channel entry */
1638 SilcChannelEntry silc_client_ref_channel(SilcClient client,
1639 SilcClientConnection conn,
1640 SilcChannelEntry channel_entry)
1642 silc_atomic_add_int16(&channel_entry->internal.refcnt, 1);
1643 SILC_LOG_DEBUG(("Channel %p refcnt %d->%d", channel_entry,
1644 silc_atomic_get_int16(&channel_entry->internal.refcnt) - 1,
1645 silc_atomic_get_int16(&channel_entry->internal.refcnt)));
1646 return channel_entry;
1649 /* Release reference of channel entry */
1651 void silc_client_unref_channel(SilcClient client, SilcClientConnection conn,
1652 SilcChannelEntry channel_entry)
1654 if (channel_entry) {
1655 SILC_LOG_DEBUG(("Channel %p refcnt %d->%d", channel_entry,
1656 silc_atomic_get_int16(&channel_entry->internal.refcnt),
1657 silc_atomic_get_int16(&channel_entry->internal.refcnt)
1659 silc_client_del_channel(client, conn, channel_entry);
1663 /* Free channel entry list */
1665 void silc_client_list_free_channels(SilcClient client,
1666 SilcClientConnection conn,
1667 SilcDList channel_list)
1669 SilcChannelEntry channel_entry;
1672 silc_dlist_start(channel_list);
1673 while ((channel_entry = silc_dlist_get(channel_list)))
1674 silc_client_unref_channel(client, conn, channel_entry);
1676 silc_dlist_uninit(channel_list);
1680 /************************* Server Searching Locally *************************/
1682 /* Finds entry for server by the server name. */
1684 SilcServerEntry silc_client_get_server(SilcClient client,
1685 SilcClientConnection conn,
1688 SilcIDCacheEntry id_cache;
1689 SilcServerEntry entry;
1691 if (!client || !conn || !server_name)
1694 SILC_LOG_DEBUG(("Find server by name %s", server_name));
1696 /* Normalize server name for search */
1697 server_name = silc_identifier_check(server_name, strlen(server_name),
1698 SILC_STRING_UTF8, 256, NULL);
1702 silc_mutex_lock(conn->internal->lock);
1704 if (!silc_idcache_find_by_name_one(conn->internal->server_cache,
1705 server_name, &id_cache)) {
1706 silc_free(server_name);
1707 silc_mutex_unlock(conn->internal->lock);
1711 SILC_LOG_DEBUG(("Found"));
1714 entry = id_cache->context;
1715 silc_client_ref_server(client, conn, entry);
1717 silc_mutex_unlock(conn->internal->lock);
1719 silc_free(server_name);
1724 /* Finds entry for server by the server ID. */
1726 SilcServerEntry silc_client_get_server_by_id(SilcClient client,
1727 SilcClientConnection conn,
1728 SilcServerID *server_id)
1730 SilcIDCacheEntry id_cache;
1731 SilcServerEntry entry;
1733 if (!client || !conn || !server_id)
1736 SILC_LOG_DEBUG(("Find server by id %s",
1737 silc_id_render(server_id, SILC_ID_SERVER)));
1739 silc_mutex_lock(conn->internal->lock);
1741 if (!silc_idcache_find_by_id_one(conn->internal->server_cache,
1742 server_id, &id_cache)) {
1743 silc_mutex_unlock(conn->internal->lock);
1747 SILC_LOG_DEBUG(("Found"));
1750 entry = id_cache->context;
1751 silc_client_ref_server(client, conn, entry);
1753 silc_mutex_unlock(conn->internal->lock);
1758 /*********************** Server Resolving from Server ***********************/
1760 /* Resolving context */
1763 SilcGetServerCallback completion;
1765 } *SilcClientGetServerInternal;
1767 /* Resolving command callback */
1769 static SilcBool silc_client_get_server_cb(SilcClient client,
1770 SilcClientConnection conn,
1771 SilcCommand command,
1777 SilcClientGetServerInternal i = context;
1778 SilcServerEntry server;
1780 if (error != SILC_STATUS_OK) {
1781 SILC_LOG_DEBUG(("Resolving failed: %s", silc_get_status_message(error)));
1783 i->completion(client, conn, error, NULL, i->context);
1787 /* Add the returned servers to list */
1788 if (i->completion) {
1789 server = va_arg(ap, SilcServerEntry);
1790 silc_client_ref_server(client, conn, server);
1791 silc_dlist_add(i->servers, server);
1792 server->internal.resolve_cmd_ident = 0;
1795 if (status == SILC_STATUS_OK || status == SILC_STATUS_LIST_END) {
1796 /* Deliver the servers to the caller */
1797 if (i->completion) {
1798 SILC_LOG_DEBUG(("Resolved %d servers", silc_dlist_count(i->servers)));
1799 silc_dlist_start(i->servers);
1800 i->completion(client, conn, SILC_STATUS_OK, i->servers, i->context);
1808 silc_client_list_free_servers(client, conn, i->servers);
1813 /* Resolve server by server ID */
1816 silc_client_get_server_by_id_resolve(SilcClient client,
1817 SilcClientConnection conn,
1818 SilcServerID *server_id,
1819 SilcGetServerCallback completion,
1822 SilcClientGetServerInternal i;
1823 SilcServerEntry server;
1825 SilcUInt16 cmd_ident;
1827 if (!client || !conn || !server_id || !completion)
1830 SILC_LOG_DEBUG(("Resolve server by id %s",
1831 silc_id_render(server_id, SILC_ID_SERVER)));
1833 i = silc_calloc(1, sizeof(*i));
1836 i->completion = completion;
1837 i->context = context;
1838 i->servers = silc_dlist_init();
1844 /* Attach to resolving, if on going */
1845 server = silc_client_get_server_by_id(client, conn, server_id);
1846 if (server && server->internal.resolve_cmd_ident) {
1847 SILC_LOG_DEBUG(("Attach to existing resolving"));
1848 silc_client_unref_server(client, conn, server);
1849 silc_client_command_pending(conn, SILC_COMMAND_NONE,
1850 server->internal.resolve_cmd_ident,
1851 silc_client_get_server_cb, i);
1852 return server->internal.resolve_cmd_ident;
1855 /* Send the command */
1856 idp = silc_id_payload_encode(server_id, SILC_ID_SERVER);
1857 cmd_ident = silc_client_command_send(client, conn, SILC_COMMAND_IDENTIFY,
1858 silc_client_get_server_cb, i, 1,
1859 5, silc_buffer_datalen(idp));
1860 silc_buffer_free(idp);
1861 if (!cmd_ident && completion)
1862 completion(client, conn, SILC_STATUS_ERR_RESOURCE_LIMIT, NULL, context);
1864 if (server && cmd_ident)
1865 server->internal.resolve_cmd_ident = cmd_ident;
1867 silc_client_unref_server(client, conn, server);
1872 /************************** Server Entry Routines ***************************/
1874 /* Add new server entry */
1876 SilcServerEntry silc_client_add_server(SilcClient client,
1877 SilcClientConnection conn,
1878 const char *server_name,
1879 const char *server_info,
1880 SilcServerID *server_id)
1882 SilcServerEntry server_entry;
1883 char *server_namec = NULL;
1888 SILC_LOG_DEBUG(("Adding new server %s", server_name));
1890 server_entry = silc_calloc(1, sizeof(*server_entry));
1894 silc_rwlock_alloc(&server_entry->internal.lock);
1895 silc_atomic_init8(&server_entry->internal.refcnt, 0);
1896 server_entry->id = *server_id;
1898 server_entry->server_name = strdup(server_name);
1900 server_entry->server_info = strdup(server_info);
1902 /* Normalize server name */
1904 server_namec = silc_identifier_check(server_name, strlen(server_name),
1905 SILC_STRING_UTF8, 256, NULL);
1906 if (!server_namec) {
1907 silc_free(server_entry->server_name);
1908 silc_free(server_entry->server_info);
1909 silc_free(server_entry);
1914 silc_mutex_lock(conn->internal->lock);
1916 /* Add server to cache */
1917 if (!silc_idcache_add(conn->internal->server_cache, server_namec,
1918 &server_entry->id, server_entry)) {
1919 silc_free(server_namec);
1920 silc_free(server_entry->server_name);
1921 silc_free(server_entry->server_info);
1922 silc_free(server_entry);
1923 silc_mutex_unlock(conn->internal->lock);
1927 silc_mutex_unlock(conn->internal->lock);
1928 silc_client_ref_server(client, conn, server_entry);
1930 SILC_LOG_DEBUG(("Added %p", server_entry));
1932 return server_entry;
1935 /* Removes server from the cache by the server entry. */
1937 SilcBool silc_client_del_server(SilcClient client, SilcClientConnection conn,
1938 SilcServerEntry server)
1945 if (silc_atomic_sub_int8(&server->internal.refcnt, 1) > 0)
1948 SILC_LOG_DEBUG(("Deleting server %p", server));
1950 silc_mutex_lock(conn->internal->lock);
1951 ret = silc_idcache_del_by_context(conn->internal->server_cache,
1953 silc_mutex_unlock(conn->internal->lock);
1955 silc_free(server->server_name);
1956 silc_free(server->server_info);
1957 if (server->public_key)
1958 silc_pkcs_public_key_free(server->public_key);
1959 silc_atomic_uninit8(&server->internal.refcnt);
1960 silc_rwlock_free(server->internal.lock);
1966 /* Updates the `server_entry' with the new information sent as argument. */
1968 void silc_client_update_server(SilcClient client,
1969 SilcClientConnection conn,
1970 SilcServerEntry server_entry,
1971 const char *server_name,
1972 const char *server_info)
1974 char *server_namec = NULL;
1976 SILC_LOG_DEBUG(("Updating server %p", server_entry));
1979 (!server_entry->server_name ||
1980 !silc_utf8_strcasecmp(server_entry->server_name, server_name))) {
1982 server_namec = silc_identifier_check(server_name, strlen(server_name),
1983 SILC_STRING_UTF8, 256, NULL);
1987 silc_free(server_entry->server_name);
1988 server_entry->server_name = strdup(server_name);
1989 if (!server_entry->server_name)
1992 /* Update cache entry */
1993 silc_mutex_lock(conn->internal->lock);
1994 silc_idcache_update_by_context(conn->internal->server_cache, server_entry,
1995 NULL, server_namec, TRUE);
1996 silc_mutex_unlock(conn->internal->lock);
2000 (!server_entry->server_info ||
2001 !silc_utf8_strcasecmp(server_entry->server_info, server_info))) {
2002 silc_free(server_entry->server_info);
2003 server_entry->server_info = strdup(server_info);
2009 void silc_client_lock_server(SilcServerEntry server_entry)
2011 silc_rwlock_rdlock(server_entry->internal.lock);
2016 void silc_client_unlock_server(SilcServerEntry server_entry)
2018 silc_rwlock_unlock(server_entry->internal.lock);
2021 /* Take reference of server entry */
2023 SilcServerEntry silc_client_ref_server(SilcClient client,
2024 SilcClientConnection conn,
2025 SilcServerEntry server_entry)
2027 silc_atomic_add_int8(&server_entry->internal.refcnt, 1);
2028 SILC_LOG_DEBUG(("Server %p refcnt %d->%d", server_entry,
2029 silc_atomic_get_int8(&server_entry->internal.refcnt) - 1,
2030 silc_atomic_get_int8(&server_entry->internal.refcnt)));
2031 return server_entry;
2034 /* Release reference of server entry */
2036 void silc_client_unref_server(SilcClient client, SilcClientConnection conn,
2037 SilcServerEntry server_entry)
2040 SILC_LOG_DEBUG(("Server %p refcnt %d->%d", server_entry,
2041 silc_atomic_get_int8(&server_entry->internal.refcnt),
2042 silc_atomic_get_int8(&server_entry->internal.refcnt)
2044 silc_client_del_server(client, conn, server_entry);
2048 /* Free server entry list */
2050 void silc_client_list_free_servers(SilcClient client,
2051 SilcClientConnection conn,
2052 SilcDList server_list)
2054 SilcServerEntry server_entry;
2057 silc_dlist_start(server_list);
2058 while ((server_entry = silc_dlist_get(server_list)))
2059 silc_client_unref_server(client, conn, server_entry);
2061 silc_dlist_uninit(server_list);