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);
906 silc_client_ftp_session_free_client(client, client_entry);
907 if (client_entry->internal.ke)
908 silc_client_abort_key_agreement(client, conn, client_entry);
909 silc_atomic_uninit8(&client_entry->internal.refcnt);
910 silc_rwlock_free(client_entry->internal.lock);
911 silc_free(client_entry);
914 /* Removes client from the cache by the client entry. */
916 SilcBool silc_client_del_client(SilcClient client, SilcClientConnection conn,
917 SilcClientEntry client_entry)
924 if (silc_atomic_sub_int8(&client_entry->internal.refcnt, 1) > 0)
927 SILC_LOG_DEBUG(("Deleting client %p", client_entry));
929 silc_mutex_lock(conn->internal->lock);
930 ret = silc_idcache_del_by_context(conn->internal->client_cache,
932 silc_mutex_unlock(conn->internal->lock);
935 /* Remove from channels */
936 silc_client_remove_from_channels(client, conn, client_entry);
938 /* Free the client entry data */
939 silc_client_del_client_entry(client, conn, client_entry);
945 /* Internal routine used to find client by ID and if not found this creates
946 new client entry and returns it. */
948 SilcClientEntry silc_client_get_client(SilcClient client,
949 SilcClientConnection conn,
950 SilcClientID *client_id)
952 SilcClientEntry client_entry;
954 client_entry = silc_client_get_client_by_id(client, conn, client_id);
956 client_entry = silc_client_add_client(client, conn, NULL, NULL, NULL,
960 silc_client_ref_client(client, conn, client_entry);
968 void silc_client_lock_client(SilcClientEntry client_entry)
970 silc_rwlock_rdlock(client_entry->internal.lock);
975 void silc_client_unlock_client(SilcClientEntry client_entry)
977 silc_rwlock_unlock(client_entry->internal.lock);
980 /* Take reference of client entry */
982 SilcClientEntry silc_client_ref_client(SilcClient client,
983 SilcClientConnection conn,
984 SilcClientEntry client_entry)
986 silc_atomic_add_int8(&client_entry->internal.refcnt, 1);
987 SILC_LOG_DEBUG(("Client %p refcnt %d->%d", client_entry,
988 silc_atomic_get_int8(&client_entry->internal.refcnt) - 1,
989 silc_atomic_get_int8(&client_entry->internal.refcnt)));
993 /* Release reference of client entry */
995 void silc_client_unref_client(SilcClient client, SilcClientConnection conn,
996 SilcClientEntry client_entry)
999 SILC_LOG_DEBUG(("Client %p refcnt %d->%d", client_entry,
1000 silc_atomic_get_int8(&client_entry->internal.refcnt),
1001 silc_atomic_get_int8(&client_entry->internal.refcnt) - 1));
1002 silc_client_del_client(client, conn, client_entry);
1006 /* Free client entry list */
1008 void silc_client_list_free(SilcClient client, SilcClientConnection conn,
1009 SilcDList client_list)
1011 SilcClientEntry client_entry;
1014 silc_dlist_start(client_list);
1015 while ((client_entry = silc_dlist_get(client_list)))
1016 silc_client_unref_client(client, conn, client_entry);
1018 silc_dlist_uninit(client_list);
1022 /* Formats the nickname of the client specified by the `client_entry'.
1023 If the format is specified by the application this will format the
1024 nickname and replace the old nickname in the client entry. If the
1025 format string is not specified then this function has no effect.
1026 Returns the client entry that was formatted. */
1028 SilcClientEntry silc_client_nickname_format(SilcClient client,
1029 SilcClientConnection conn,
1030 SilcClientEntry client_entry,
1034 char newnick[128 + 1];
1035 int i, off = 0, len;
1038 SilcClientEntry entry, unformatted = NULL;
1040 if (!client->internal->params->nickname_format[0])
1041 return client_entry;
1042 if (!client_entry->nickname[0])
1045 SILC_LOG_DEBUG(("Format nickname"));
1047 /* Get all clients with same nickname. Do not perform the formatting
1048 if there aren't any clients with same nickname unless the application
1049 is forcing us to do so. */
1050 clients = silc_client_get_clients_local(client, conn,
1051 client_entry->nickname, NULL);
1054 if (silc_dlist_count(clients) == 1 &&
1055 !client->internal->params->nickname_force_format) {
1056 silc_client_list_free(client, conn, clients);
1057 return client_entry;
1062 while ((entry = silc_dlist_get(clients))) {
1063 if (entry->internal.valid && entry != client_entry)
1065 if (entry->internal.valid && entry != client_entry &&
1066 silc_utf8_strcasecmp(entry->nickname, client_entry->nickname)) {
1068 unformatted = entry;
1072 if (!len || freebase) {
1073 silc_client_list_free(client, conn, clients);
1074 return client_entry;
1077 /* If priority formatting, this client always gets unformatted nickname. */
1078 if (unformatted && priority)
1079 client_entry = unformatted;
1081 memset(newnick, 0, sizeof(newnick));
1082 cp = client->internal->params->nickname_format;
1092 if (!client_entry->nickname[0])
1094 len = strlen(client_entry->nickname);
1095 memcpy(&newnick[off], client_entry->nickname, len);
1099 /* Stripped hostname */
1100 if (!client_entry->hostname[0])
1102 len = strcspn(client_entry->hostname, ".");
1103 i = strcspn(client_entry->hostname, "-");
1106 memcpy(&newnick[off], client_entry->hostname, len);
1111 if (!client_entry->hostname[0])
1113 len = strlen(client_entry->hostname);
1114 memcpy(&newnick[off], client_entry->hostname, len);
1118 /* Stripped server name */
1119 if (!client_entry->server)
1121 len = strcspn(client_entry->server, ".");
1122 memcpy(&newnick[off], client_entry->server, len);
1126 /* Full server name */
1127 if (!client_entry->server)
1129 len = strlen(client_entry->server);
1130 memcpy(&newnick[off], client_entry->server, len);
1134 /* Ascending number */
1139 if (silc_dlist_count(clients) == 1)
1142 silc_dlist_start(clients);
1143 while ((entry = silc_dlist_get(clients))) {
1144 if (!silc_utf8_strncasecmp(entry->nickname, newnick, off))
1146 if (strlen(entry->nickname) <= off)
1148 num = atoi(&entry->nickname[off]);
1153 memset(tmp, 0, sizeof(tmp));
1154 silc_snprintf(tmp, sizeof(tmp) - 1, "%d", ++max);
1156 memcpy(&newnick[off], tmp, len);
1161 /* Some other character in the string */
1162 memcpy(&newnick[off], cp, 1);
1171 memcpy(client_entry->nickname, newnick, strlen(newnick));
1172 silc_client_list_free(client, conn, clients);
1174 return client_entry;
1177 /* Parses nickname according to nickname format string */
1179 SilcBool silc_client_nickname_parse(SilcClient client,
1180 SilcClientConnection conn,
1184 char *cp, s = 0, e = 0, *nick;
1188 if (!client->internal->params->nickname_format[0])
1191 if (!nickname || !nickname[0])
1194 cp = client->internal->params->nickname_format;
1214 /* Get separator character */
1227 /* Parse the nickname */
1231 if (strchr(nickname, s))
1232 nick = strchr(nickname, s) + 1;
1234 if (strchr(nick, e))
1235 len = strchr(nick, e) - nick;
1239 *ret_nick = silc_memdup(nick, len);
1243 SILC_LOG_DEBUG(("Parsed nickname: %s", *ret_nick));
1248 /************************ Channel Searching Locally *************************/
1250 /* Finds entry for channel by the channel name. Returns the entry or NULL
1251 if the entry was not found. It is found only if the client is joined
1254 SilcChannelEntry silc_client_get_channel(SilcClient client,
1255 SilcClientConnection conn,
1258 SilcIDCacheEntry id_cache;
1259 SilcChannelEntry entry;
1261 if (!client || !conn || !channel)
1264 SILC_LOG_DEBUG(("Find channel %s", channel));
1266 /* Normalize name for search */
1267 channel = silc_channel_name_check(channel, strlen(channel), SILC_STRING_UTF8,
1272 silc_mutex_lock(conn->internal->lock);
1274 if (!silc_idcache_find_by_name_one(conn->internal->channel_cache, channel,
1276 silc_mutex_unlock(conn->internal->lock);
1281 SILC_LOG_DEBUG(("Found"));
1283 entry = id_cache->context;
1286 silc_client_ref_channel(client, conn, entry);
1287 silc_mutex_unlock(conn->internal->lock);
1294 /* Finds entry for channel by the channel ID. Returns the entry or NULL
1295 if the entry was not found. It is found only if the client is joined
1298 SilcChannelEntry silc_client_get_channel_by_id(SilcClient client,
1299 SilcClientConnection conn,
1300 SilcChannelID *channel_id)
1302 SilcIDCacheEntry id_cache;
1303 SilcChannelEntry entry;
1305 if (!client || !conn || !channel_id)
1308 SILC_LOG_DEBUG(("Find channel by id %s",
1309 silc_id_render(channel_id, SILC_ID_CHANNEL)));
1311 silc_mutex_lock(conn->internal->lock);
1313 if (!silc_idcache_find_by_id_one(conn->internal->channel_cache, channel_id,
1315 silc_mutex_unlock(conn->internal->lock);
1319 SILC_LOG_DEBUG(("Found"));
1321 entry = id_cache->context;
1324 silc_client_ref_channel(client, conn, entry);
1325 silc_mutex_unlock(conn->internal->lock);
1330 /********************** Channel Resolving from Server ***********************/
1332 /* Channel resolving context */
1335 SilcGetChannelCallback completion;
1337 } *SilcClientGetChannelInternal;
1339 /* Resolving command callback */
1341 static SilcBool silc_client_get_channel_cb(SilcClient client,
1342 SilcClientConnection conn,
1343 SilcCommand command,
1349 SilcClientGetChannelInternal i = context;
1350 SilcChannelEntry entry;
1352 if (error != SILC_STATUS_OK) {
1353 SILC_LOG_DEBUG(("Resolving failed: %s", silc_get_status_message(error)));
1355 i->completion(client, conn, error, NULL, i->context);
1359 /* Add the returned channel to list */
1360 if (i->completion) {
1361 entry = va_arg(ap, SilcChannelEntry);
1362 silc_client_ref_channel(client, conn, entry);
1363 silc_dlist_add(i->channels, entry);
1366 if (status == SILC_STATUS_OK || status == SILC_STATUS_LIST_END) {
1367 /* Deliver the channels to the caller */
1368 if (i->completion) {
1369 SILC_LOG_DEBUG(("Resolved %d channels", silc_dlist_count(i->channels)));
1370 silc_dlist_start(i->channels);
1371 i->completion(client, conn, SILC_STATUS_OK, i->channels, i->context);
1379 silc_client_list_free_channels(client, conn, i->channels);
1384 /* Resolves channel entry from the server by the channel name. */
1386 void silc_client_get_channel_resolve(SilcClient client,
1387 SilcClientConnection conn,
1389 SilcGetChannelCallback completion,
1392 SilcClientGetChannelInternal i;
1394 if (!client || !conn || !channel_name || !completion)
1397 SILC_LOG_DEBUG(("Resolve channel %s", channel_name));
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 if (!silc_client_command_send(client, conn, SILC_COMMAND_IDENTIFY,
1412 silc_client_get_channel_cb, i, 1,
1413 3, channel_name, strlen(channel_name))) {
1415 completion(client, conn, SILC_STATUS_ERR_RESOURCE_LIMIT, NULL, context);
1419 /* Resolves channel information from the server by the channel ID. */
1422 silc_client_get_channel_by_id_resolve(SilcClient client,
1423 SilcClientConnection conn,
1424 SilcChannelID *channel_id,
1425 SilcGetChannelCallback completion,
1428 SilcClientGetChannelInternal i;
1430 SilcUInt16 cmd_ident;
1432 if (!client || !conn || !channel_id || !completion)
1435 SILC_LOG_DEBUG(("Resolve channel by id %s",
1436 silc_id_render(channel_id, SILC_ID_CHANNEL)));
1438 i = silc_calloc(1, sizeof(*i));
1441 i->completion = completion;
1442 i->context = context;
1443 i->channels = silc_dlist_init();
1449 /* Send the command */
1450 idp = silc_id_payload_encode(channel_id, SILC_ID_CHANNEL);
1451 cmd_ident = silc_client_command_send(client, conn, SILC_COMMAND_IDENTIFY,
1452 silc_client_get_channel_cb, i, 1,
1453 5, silc_buffer_datalen(idp));
1454 silc_buffer_free(idp);
1455 if (!cmd_ident && completion)
1456 completion(client, conn, SILC_STATUS_ERR_RESOURCE_LIMIT, NULL, context);
1461 /************************* Channel Entry Routines ***************************/
1463 /* Add new channel entry to the ID Cache */
1465 SilcChannelEntry silc_client_add_channel(SilcClient client,
1466 SilcClientConnection conn,
1467 const char *channel_name,
1469 SilcChannelID *channel_id)
1471 SilcChannelEntry channel;
1472 char *channel_namec;
1474 SILC_LOG_DEBUG(("Start"));
1476 channel = silc_calloc(1, sizeof(*channel));
1480 silc_rwlock_alloc(&channel->internal.lock);
1481 silc_atomic_init16(&channel->internal.refcnt, 0);
1482 channel->id = *channel_id;
1483 channel->mode = mode;
1485 channel->channel_name = strdup(channel_name);
1486 if (!channel->channel_name) {
1491 channel->user_list = silc_hash_table_alloc(1, silc_hash_ptr, NULL, NULL,
1492 NULL, NULL, NULL, TRUE);
1493 if (!channel->user_list) {
1494 silc_free(channel->channel_name);
1499 /* Normalize channel name */
1500 channel_namec = silc_channel_name_check(channel_name, strlen(channel_name),
1501 SILC_STRING_UTF8, 256, NULL);
1502 if (!channel_namec) {
1503 silc_free(channel->channel_name);
1504 silc_hash_table_free(channel->user_list);
1509 silc_mutex_lock(conn->internal->lock);
1511 /* Add channel to cache, the normalized channel name is saved to cache */
1512 if (!silc_idcache_add(conn->internal->channel_cache, channel_namec,
1513 &channel->id, channel)) {
1514 silc_free(channel_namec);
1515 silc_free(channel->channel_name);
1516 silc_hash_table_free(channel->user_list);
1518 silc_mutex_unlock(conn->internal->lock);
1522 silc_mutex_unlock(conn->internal->lock);
1523 silc_client_ref_channel(client, conn, channel);
1525 SILC_LOG_DEBUG(("Added %p", channel));
1530 /* Removes channel from the cache by the channel entry. */
1532 SilcBool silc_client_del_channel(SilcClient client, SilcClientConnection conn,
1533 SilcChannelEntry channel)
1535 SilcIDCacheEntry id_cache;
1543 if (silc_atomic_sub_int16(&channel->internal.refcnt, 1) > 0)
1546 SILC_LOG_DEBUG(("Deleting channel %p", channel));
1548 silc_mutex_lock(conn->internal->lock);
1549 if (silc_idcache_find_by_context(conn->internal->channel_cache, channel,
1551 silc_free(id_cache->name);
1552 ret = silc_idcache_del_by_context(conn->internal->channel_cache,
1554 silc_mutex_unlock(conn->internal->lock);
1559 silc_client_empty_channel(client, conn, channel);
1560 silc_hash_table_free(channel->user_list);
1561 silc_free(channel->channel_name);
1562 silc_free(channel->topic);
1563 if (channel->founder_key)
1564 silc_pkcs_public_key_free(channel->founder_key);
1565 if (channel->internal.send_key)
1566 silc_cipher_free(channel->internal.send_key);
1567 if (channel->internal.receive_key)
1568 silc_cipher_free(channel->internal.receive_key);
1569 if (channel->internal.hmac)
1570 silc_hmac_free(channel->internal.hmac);
1571 if (channel->internal.old_channel_keys) {
1572 silc_dlist_start(channel->internal.old_channel_keys);
1573 while ((key = silc_dlist_get(channel->internal.old_channel_keys)))
1574 silc_cipher_free(key);
1575 silc_dlist_uninit(channel->internal.old_channel_keys);
1577 if (channel->internal.old_hmacs) {
1578 silc_dlist_start(channel->internal.old_hmacs);
1579 while ((hmac = silc_dlist_get(channel->internal.old_hmacs)))
1580 silc_hmac_free(hmac);
1581 silc_dlist_uninit(channel->internal.old_hmacs);
1583 if (channel->channel_pubkeys)
1584 silc_argument_list_free(channel->channel_pubkeys,
1585 SILC_ARGUMENT_PUBLIC_KEY);
1586 silc_client_del_channel_private_keys(client, conn, channel);
1587 silc_atomic_uninit16(&channel->internal.refcnt);
1588 silc_rwlock_free(channel->internal.lock);
1589 silc_schedule_task_del_by_context(conn->client->schedule, channel);
1595 /* Replaces the channel ID of the `channel' to `new_id'. Returns FALSE
1596 if the ID could not be changed. This handles entry locking internally. */
1598 SilcBool silc_client_replace_channel_id(SilcClient client,
1599 SilcClientConnection conn,
1600 SilcChannelEntry channel,
1601 SilcChannelID *new_id)
1603 SilcBool ret = FALSE;
1608 SILC_LOG_DEBUG(("Old Channel ID id(%s)",
1609 silc_id_render(&channel->id, SILC_ID_CHANNEL)));
1610 SILC_LOG_DEBUG(("New Channel ID id(%s)",
1611 silc_id_render(new_id, SILC_ID_CHANNEL)));
1614 silc_rwlock_wrlock(channel->internal.lock);
1615 silc_mutex_lock(conn->internal->lock);
1616 silc_idcache_update_by_context(conn->internal->channel_cache, channel,
1617 new_id, NULL, FALSE);
1618 silc_mutex_unlock(conn->internal->lock);
1619 silc_rwlock_unlock(channel->internal.lock);
1626 void silc_client_lock_channel(SilcChannelEntry channel_entry)
1628 silc_rwlock_rdlock(channel_entry->internal.lock);
1631 /* Unlock channel */
1633 void silc_client_unlock_channel(SilcChannelEntry channel_entry)
1635 silc_rwlock_unlock(channel_entry->internal.lock);
1638 /* Take reference of channel entry */
1640 SilcChannelEntry silc_client_ref_channel(SilcClient client,
1641 SilcClientConnection conn,
1642 SilcChannelEntry channel_entry)
1644 silc_atomic_add_int16(&channel_entry->internal.refcnt, 1);
1645 SILC_LOG_DEBUG(("Channel %p refcnt %d->%d", channel_entry,
1646 silc_atomic_get_int16(&channel_entry->internal.refcnt) - 1,
1647 silc_atomic_get_int16(&channel_entry->internal.refcnt)));
1648 return channel_entry;
1651 /* Release reference of channel entry */
1653 void silc_client_unref_channel(SilcClient client, SilcClientConnection conn,
1654 SilcChannelEntry channel_entry)
1656 if (channel_entry) {
1657 SILC_LOG_DEBUG(("Channel %p refcnt %d->%d", channel_entry,
1658 silc_atomic_get_int16(&channel_entry->internal.refcnt),
1659 silc_atomic_get_int16(&channel_entry->internal.refcnt)
1661 silc_client_del_channel(client, conn, channel_entry);
1665 /* Free channel entry list */
1667 void silc_client_list_free_channels(SilcClient client,
1668 SilcClientConnection conn,
1669 SilcDList channel_list)
1671 SilcChannelEntry channel_entry;
1674 silc_dlist_start(channel_list);
1675 while ((channel_entry = silc_dlist_get(channel_list)))
1676 silc_client_unref_channel(client, conn, channel_entry);
1678 silc_dlist_uninit(channel_list);
1682 /************************* Server Searching Locally *************************/
1684 /* Finds entry for server by the server name. */
1686 SilcServerEntry silc_client_get_server(SilcClient client,
1687 SilcClientConnection conn,
1690 SilcIDCacheEntry id_cache;
1691 SilcServerEntry entry;
1693 if (!client || !conn || !server_name)
1696 SILC_LOG_DEBUG(("Find server by name %s", server_name));
1698 /* Normalize server name for search */
1699 server_name = silc_identifier_check(server_name, strlen(server_name),
1700 SILC_STRING_UTF8, 256, NULL);
1704 silc_mutex_lock(conn->internal->lock);
1706 if (!silc_idcache_find_by_name_one(conn->internal->server_cache,
1707 server_name, &id_cache)) {
1708 silc_free(server_name);
1709 silc_mutex_unlock(conn->internal->lock);
1713 SILC_LOG_DEBUG(("Found"));
1716 entry = id_cache->context;
1717 silc_client_ref_server(client, conn, entry);
1719 silc_mutex_unlock(conn->internal->lock);
1721 silc_free(server_name);
1726 /* Finds entry for server by the server ID. */
1728 SilcServerEntry silc_client_get_server_by_id(SilcClient client,
1729 SilcClientConnection conn,
1730 SilcServerID *server_id)
1732 SilcIDCacheEntry id_cache;
1733 SilcServerEntry entry;
1735 if (!client || !conn || !server_id)
1738 SILC_LOG_DEBUG(("Find server by id %s",
1739 silc_id_render(server_id, SILC_ID_SERVER)));
1741 silc_mutex_lock(conn->internal->lock);
1743 if (!silc_idcache_find_by_id_one(conn->internal->server_cache,
1744 server_id, &id_cache)) {
1745 silc_mutex_unlock(conn->internal->lock);
1749 SILC_LOG_DEBUG(("Found"));
1752 entry = id_cache->context;
1753 silc_client_ref_server(client, conn, entry);
1755 silc_mutex_unlock(conn->internal->lock);
1760 /*********************** Server Resolving from Server ***********************/
1762 /* Resolving context */
1765 SilcGetServerCallback completion;
1767 } *SilcClientGetServerInternal;
1769 /* Resolving command callback */
1771 static SilcBool silc_client_get_server_cb(SilcClient client,
1772 SilcClientConnection conn,
1773 SilcCommand command,
1779 SilcClientGetServerInternal i = context;
1780 SilcServerEntry server;
1782 if (error != SILC_STATUS_OK) {
1783 SILC_LOG_DEBUG(("Resolving failed: %s", silc_get_status_message(error)));
1785 i->completion(client, conn, error, NULL, i->context);
1789 /* Add the returned servers to list */
1790 if (i->completion) {
1791 server = va_arg(ap, SilcServerEntry);
1792 silc_client_ref_server(client, conn, server);
1793 silc_dlist_add(i->servers, server);
1794 server->internal.resolve_cmd_ident = 0;
1797 if (status == SILC_STATUS_OK || status == SILC_STATUS_LIST_END) {
1798 /* Deliver the servers to the caller */
1799 if (i->completion) {
1800 SILC_LOG_DEBUG(("Resolved %d servers", silc_dlist_count(i->servers)));
1801 silc_dlist_start(i->servers);
1802 i->completion(client, conn, SILC_STATUS_OK, i->servers, i->context);
1810 silc_client_list_free_servers(client, conn, i->servers);
1815 /* Resolve server by server ID */
1818 silc_client_get_server_by_id_resolve(SilcClient client,
1819 SilcClientConnection conn,
1820 SilcServerID *server_id,
1821 SilcGetServerCallback completion,
1824 SilcClientGetServerInternal i;
1825 SilcServerEntry server;
1827 SilcUInt16 cmd_ident;
1829 if (!client || !conn || !server_id || !completion)
1832 SILC_LOG_DEBUG(("Resolve server by id %s",
1833 silc_id_render(server_id, SILC_ID_SERVER)));
1835 i = silc_calloc(1, sizeof(*i));
1838 i->completion = completion;
1839 i->context = context;
1840 i->servers = silc_dlist_init();
1846 /* Attach to resolving, if on going */
1847 server = silc_client_get_server_by_id(client, conn, server_id);
1848 if (server && server->internal.resolve_cmd_ident) {
1849 SILC_LOG_DEBUG(("Attach to existing resolving"));
1850 silc_client_unref_server(client, conn, server);
1851 silc_client_command_pending(conn, SILC_COMMAND_NONE,
1852 server->internal.resolve_cmd_ident,
1853 silc_client_get_server_cb, i);
1854 return server->internal.resolve_cmd_ident;
1857 /* Send the command */
1858 idp = silc_id_payload_encode(server_id, SILC_ID_SERVER);
1859 cmd_ident = silc_client_command_send(client, conn, SILC_COMMAND_IDENTIFY,
1860 silc_client_get_server_cb, i, 1,
1861 5, silc_buffer_datalen(idp));
1862 silc_buffer_free(idp);
1863 if (!cmd_ident && completion)
1864 completion(client, conn, SILC_STATUS_ERR_RESOURCE_LIMIT, NULL, context);
1866 if (server && cmd_ident)
1867 server->internal.resolve_cmd_ident = cmd_ident;
1869 silc_client_unref_server(client, conn, server);
1874 /************************** Server Entry Routines ***************************/
1876 /* Add new server entry */
1878 SilcServerEntry silc_client_add_server(SilcClient client,
1879 SilcClientConnection conn,
1880 const char *server_name,
1881 const char *server_info,
1882 SilcServerID *server_id)
1884 SilcServerEntry server_entry;
1885 char *server_namec = NULL;
1890 SILC_LOG_DEBUG(("Adding new server %s", server_name));
1892 server_entry = silc_calloc(1, sizeof(*server_entry));
1896 silc_rwlock_alloc(&server_entry->internal.lock);
1897 silc_atomic_init8(&server_entry->internal.refcnt, 0);
1898 server_entry->id = *server_id;
1900 server_entry->server_name = strdup(server_name);
1902 server_entry->server_info = strdup(server_info);
1904 /* Normalize server name */
1906 server_namec = silc_identifier_check(server_name, strlen(server_name),
1907 SILC_STRING_UTF8, 256, NULL);
1908 if (!server_namec) {
1909 silc_free(server_entry->server_name);
1910 silc_free(server_entry->server_info);
1911 silc_free(server_entry);
1916 silc_mutex_lock(conn->internal->lock);
1918 /* Add server to cache */
1919 if (!silc_idcache_add(conn->internal->server_cache, server_namec,
1920 &server_entry->id, server_entry)) {
1921 silc_free(server_namec);
1922 silc_free(server_entry->server_name);
1923 silc_free(server_entry->server_info);
1924 silc_free(server_entry);
1925 silc_mutex_unlock(conn->internal->lock);
1929 silc_mutex_unlock(conn->internal->lock);
1930 silc_client_ref_server(client, conn, server_entry);
1932 SILC_LOG_DEBUG(("Added %p", server_entry));
1934 return server_entry;
1937 /* Removes server from the cache by the server entry. */
1939 SilcBool silc_client_del_server(SilcClient client, SilcClientConnection conn,
1940 SilcServerEntry server)
1942 SilcIDCacheEntry id_cache;
1948 if (silc_atomic_sub_int8(&server->internal.refcnt, 1) > 0)
1951 SILC_LOG_DEBUG(("Deleting server %p", server));
1953 silc_mutex_lock(conn->internal->lock);
1954 if (silc_idcache_find_by_context(conn->internal->server_cache, server,
1956 silc_free(id_cache->name);
1957 ret = silc_idcache_del_by_context(conn->internal->server_cache,
1959 silc_mutex_unlock(conn->internal->lock);
1961 silc_free(server->server_name);
1962 silc_free(server->server_info);
1963 if (server->public_key)
1964 silc_pkcs_public_key_free(server->public_key);
1965 silc_atomic_uninit8(&server->internal.refcnt);
1966 silc_rwlock_free(server->internal.lock);
1972 /* Updates the `server_entry' with the new information sent as argument. */
1974 void silc_client_update_server(SilcClient client,
1975 SilcClientConnection conn,
1976 SilcServerEntry server_entry,
1977 const char *server_name,
1978 const char *server_info)
1980 char *server_namec = NULL;
1982 SILC_LOG_DEBUG(("Updating server %p", server_entry));
1985 (!server_entry->server_name ||
1986 !silc_utf8_strcasecmp(server_entry->server_name, server_name))) {
1988 server_namec = silc_identifier_check(server_name, strlen(server_name),
1989 SILC_STRING_UTF8, 256, NULL);
1993 silc_free(server_entry->server_name);
1994 server_entry->server_name = strdup(server_name);
1995 if (!server_entry->server_name)
1998 /* Update cache entry */
1999 silc_mutex_lock(conn->internal->lock);
2000 silc_idcache_update_by_context(conn->internal->server_cache, server_entry,
2001 NULL, server_namec, TRUE);
2002 silc_mutex_unlock(conn->internal->lock);
2006 (!server_entry->server_info ||
2007 !silc_utf8_strcasecmp(server_entry->server_info, server_info))) {
2008 silc_free(server_entry->server_info);
2009 server_entry->server_info = strdup(server_info);
2015 void silc_client_lock_server(SilcServerEntry server_entry)
2017 silc_rwlock_rdlock(server_entry->internal.lock);
2022 void silc_client_unlock_server(SilcServerEntry server_entry)
2024 silc_rwlock_unlock(server_entry->internal.lock);
2027 /* Take reference of server entry */
2029 SilcServerEntry silc_client_ref_server(SilcClient client,
2030 SilcClientConnection conn,
2031 SilcServerEntry server_entry)
2033 silc_atomic_add_int8(&server_entry->internal.refcnt, 1);
2034 SILC_LOG_DEBUG(("Server %p refcnt %d->%d", server_entry,
2035 silc_atomic_get_int8(&server_entry->internal.refcnt) - 1,
2036 silc_atomic_get_int8(&server_entry->internal.refcnt)));
2037 return server_entry;
2040 /* Release reference of server entry */
2042 void silc_client_unref_server(SilcClient client, SilcClientConnection conn,
2043 SilcServerEntry server_entry)
2046 SILC_LOG_DEBUG(("Server %p refcnt %d->%d", server_entry,
2047 silc_atomic_get_int8(&server_entry->internal.refcnt),
2048 silc_atomic_get_int8(&server_entry->internal.refcnt)
2050 silc_client_del_server(client, conn, server_entry);
2054 /* Free server entry list */
2056 void silc_client_list_free_servers(SilcClient client,
2057 SilcClientConnection conn,
2058 SilcDList server_list)
2060 SilcServerEntry server_entry;
2063 silc_dlist_start(server_list);
2064 while ((server_entry = silc_dlist_get(server_list)))
2065 silc_client_unref_server(client, conn, server_entry);
2067 silc_dlist_uninit(server_list);