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)
1542 if (silc_atomic_sub_int16(&channel->internal.refcnt, 1) > 0)
1545 SILC_LOG_DEBUG(("Deleting channel %p", channel));
1547 silc_mutex_lock(conn->internal->lock);
1548 ret = silc_idcache_del_by_context(conn->internal->channel_cache,
1550 silc_mutex_unlock(conn->internal->lock);
1555 silc_client_empty_channel(client, conn, channel);
1556 silc_hash_table_free(channel->user_list);
1557 silc_free(channel->channel_name);
1558 silc_free(channel->topic);
1559 if (channel->founder_key)
1560 silc_pkcs_public_key_free(channel->founder_key);
1561 if (channel->internal.send_key)
1562 silc_cipher_free(channel->internal.send_key);
1563 if (channel->internal.receive_key)
1564 silc_cipher_free(channel->internal.receive_key);
1565 if (channel->internal.hmac)
1566 silc_hmac_free(channel->internal.hmac);
1567 if (channel->internal.old_channel_keys) {
1568 silc_dlist_start(channel->internal.old_channel_keys);
1569 while ((key = silc_dlist_get(channel->internal.old_channel_keys)))
1570 silc_cipher_free(key);
1571 silc_dlist_uninit(channel->internal.old_channel_keys);
1573 if (channel->internal.old_hmacs) {
1574 silc_dlist_start(channel->internal.old_hmacs);
1575 while ((hmac = silc_dlist_get(channel->internal.old_hmacs)))
1576 silc_hmac_free(hmac);
1577 silc_dlist_uninit(channel->internal.old_hmacs);
1579 if (channel->channel_pubkeys)
1580 silc_argument_list_free(channel->channel_pubkeys,
1581 SILC_ARGUMENT_PUBLIC_KEY);
1582 silc_client_del_channel_private_keys(client, conn, channel);
1583 silc_atomic_uninit16(&channel->internal.refcnt);
1584 silc_rwlock_free(channel->internal.lock);
1585 silc_schedule_task_del_by_context(conn->client->schedule, channel);
1591 /* Replaces the channel ID of the `channel' to `new_id'. Returns FALSE
1592 if the ID could not be changed. This handles entry locking internally. */
1594 SilcBool silc_client_replace_channel_id(SilcClient client,
1595 SilcClientConnection conn,
1596 SilcChannelEntry channel,
1597 SilcChannelID *new_id)
1599 SilcBool ret = FALSE;
1604 SILC_LOG_DEBUG(("Old Channel ID id(%s)",
1605 silc_id_render(&channel->id, SILC_ID_CHANNEL)));
1606 SILC_LOG_DEBUG(("New Channel ID id(%s)",
1607 silc_id_render(new_id, SILC_ID_CHANNEL)));
1610 silc_rwlock_wrlock(channel->internal.lock);
1611 silc_mutex_lock(conn->internal->lock);
1612 silc_idcache_update_by_context(conn->internal->channel_cache, channel,
1613 new_id, NULL, FALSE);
1614 silc_mutex_unlock(conn->internal->lock);
1615 silc_rwlock_unlock(channel->internal.lock);
1622 void silc_client_lock_channel(SilcChannelEntry channel_entry)
1624 silc_rwlock_rdlock(channel_entry->internal.lock);
1627 /* Unlock channel */
1629 void silc_client_unlock_channel(SilcChannelEntry channel_entry)
1631 silc_rwlock_unlock(channel_entry->internal.lock);
1634 /* Take reference of channel entry */
1636 SilcChannelEntry silc_client_ref_channel(SilcClient client,
1637 SilcClientConnection conn,
1638 SilcChannelEntry channel_entry)
1640 silc_atomic_add_int16(&channel_entry->internal.refcnt, 1);
1641 SILC_LOG_DEBUG(("Channel %p refcnt %d->%d", channel_entry,
1642 silc_atomic_get_int16(&channel_entry->internal.refcnt) - 1,
1643 silc_atomic_get_int16(&channel_entry->internal.refcnt)));
1644 return channel_entry;
1647 /* Release reference of channel entry */
1649 void silc_client_unref_channel(SilcClient client, SilcClientConnection conn,
1650 SilcChannelEntry channel_entry)
1652 if (channel_entry) {
1653 SILC_LOG_DEBUG(("Channel %p refcnt %d->%d", channel_entry,
1654 silc_atomic_get_int16(&channel_entry->internal.refcnt),
1655 silc_atomic_get_int16(&channel_entry->internal.refcnt)
1657 silc_client_del_channel(client, conn, channel_entry);
1661 /* Free channel entry list */
1663 void silc_client_list_free_channels(SilcClient client,
1664 SilcClientConnection conn,
1665 SilcDList channel_list)
1667 SilcChannelEntry channel_entry;
1670 silc_dlist_start(channel_list);
1671 while ((channel_entry = silc_dlist_get(channel_list)))
1672 silc_client_unref_channel(client, conn, channel_entry);
1674 silc_dlist_uninit(channel_list);
1678 /************************* Server Searching Locally *************************/
1680 /* Finds entry for server by the server name. */
1682 SilcServerEntry silc_client_get_server(SilcClient client,
1683 SilcClientConnection conn,
1686 SilcIDCacheEntry id_cache;
1687 SilcServerEntry entry;
1689 if (!client || !conn || !server_name)
1692 SILC_LOG_DEBUG(("Find server by name %s", server_name));
1694 /* Normalize server name for search */
1695 server_name = silc_identifier_check(server_name, strlen(server_name),
1696 SILC_STRING_UTF8, 256, NULL);
1700 silc_mutex_lock(conn->internal->lock);
1702 if (!silc_idcache_find_by_name_one(conn->internal->server_cache,
1703 server_name, &id_cache)) {
1704 silc_free(server_name);
1705 silc_mutex_unlock(conn->internal->lock);
1709 SILC_LOG_DEBUG(("Found"));
1712 entry = id_cache->context;
1713 silc_client_ref_server(client, conn, entry);
1715 silc_mutex_unlock(conn->internal->lock);
1717 silc_free(server_name);
1722 /* Finds entry for server by the server ID. */
1724 SilcServerEntry silc_client_get_server_by_id(SilcClient client,
1725 SilcClientConnection conn,
1726 SilcServerID *server_id)
1728 SilcIDCacheEntry id_cache;
1729 SilcServerEntry entry;
1731 if (!client || !conn || !server_id)
1734 SILC_LOG_DEBUG(("Find server by id %s",
1735 silc_id_render(server_id, SILC_ID_SERVER)));
1737 silc_mutex_lock(conn->internal->lock);
1739 if (!silc_idcache_find_by_id_one(conn->internal->server_cache,
1740 server_id, &id_cache)) {
1741 silc_mutex_unlock(conn->internal->lock);
1745 SILC_LOG_DEBUG(("Found"));
1748 entry = id_cache->context;
1749 silc_client_ref_server(client, conn, entry);
1751 silc_mutex_unlock(conn->internal->lock);
1756 /*********************** Server Resolving from Server ***********************/
1758 /* Resolving context */
1761 SilcGetServerCallback completion;
1763 } *SilcClientGetServerInternal;
1765 /* Resolving command callback */
1767 static SilcBool silc_client_get_server_cb(SilcClient client,
1768 SilcClientConnection conn,
1769 SilcCommand command,
1775 SilcClientGetServerInternal i = context;
1776 SilcServerEntry server;
1778 if (error != SILC_STATUS_OK) {
1779 SILC_LOG_DEBUG(("Resolving failed: %s", silc_get_status_message(error)));
1781 i->completion(client, conn, error, NULL, i->context);
1785 /* Add the returned servers to list */
1786 if (i->completion) {
1787 server = va_arg(ap, SilcServerEntry);
1788 silc_client_ref_server(client, conn, server);
1789 silc_dlist_add(i->servers, server);
1790 server->internal.resolve_cmd_ident = 0;
1793 if (status == SILC_STATUS_OK || status == SILC_STATUS_LIST_END) {
1794 /* Deliver the servers to the caller */
1795 if (i->completion) {
1796 SILC_LOG_DEBUG(("Resolved %d servers", silc_dlist_count(i->servers)));
1797 silc_dlist_start(i->servers);
1798 i->completion(client, conn, SILC_STATUS_OK, i->servers, i->context);
1806 silc_client_list_free_servers(client, conn, i->servers);
1811 /* Resolve server by server ID */
1814 silc_client_get_server_by_id_resolve(SilcClient client,
1815 SilcClientConnection conn,
1816 SilcServerID *server_id,
1817 SilcGetServerCallback completion,
1820 SilcClientGetServerInternal i;
1821 SilcServerEntry server;
1823 SilcUInt16 cmd_ident;
1825 if (!client || !conn || !server_id || !completion)
1828 SILC_LOG_DEBUG(("Resolve server by id %s",
1829 silc_id_render(server_id, SILC_ID_SERVER)));
1831 i = silc_calloc(1, sizeof(*i));
1834 i->completion = completion;
1835 i->context = context;
1836 i->servers = silc_dlist_init();
1842 /* Attach to resolving, if on going */
1843 server = silc_client_get_server_by_id(client, conn, server_id);
1844 if (server && server->internal.resolve_cmd_ident) {
1845 SILC_LOG_DEBUG(("Attach to existing resolving"));
1846 silc_client_unref_server(client, conn, server);
1847 silc_client_command_pending(conn, SILC_COMMAND_NONE,
1848 server->internal.resolve_cmd_ident,
1849 silc_client_get_server_cb, i);
1850 return server->internal.resolve_cmd_ident;
1853 /* Send the command */
1854 idp = silc_id_payload_encode(server_id, SILC_ID_SERVER);
1855 cmd_ident = silc_client_command_send(client, conn, SILC_COMMAND_IDENTIFY,
1856 silc_client_get_server_cb, i, 1,
1857 5, silc_buffer_datalen(idp));
1858 silc_buffer_free(idp);
1859 if (!cmd_ident && completion)
1860 completion(client, conn, SILC_STATUS_ERR_RESOURCE_LIMIT, NULL, context);
1862 if (server && cmd_ident)
1863 server->internal.resolve_cmd_ident = cmd_ident;
1865 silc_client_unref_server(client, conn, server);
1870 /************************** Server Entry Routines ***************************/
1872 /* Add new server entry */
1874 SilcServerEntry silc_client_add_server(SilcClient client,
1875 SilcClientConnection conn,
1876 const char *server_name,
1877 const char *server_info,
1878 SilcServerID *server_id)
1880 SilcServerEntry server_entry;
1881 char *server_namec = NULL;
1886 SILC_LOG_DEBUG(("Adding new server %s", server_name));
1888 server_entry = silc_calloc(1, sizeof(*server_entry));
1892 silc_rwlock_alloc(&server_entry->internal.lock);
1893 silc_atomic_init8(&server_entry->internal.refcnt, 0);
1894 server_entry->id = *server_id;
1896 server_entry->server_name = strdup(server_name);
1898 server_entry->server_info = strdup(server_info);
1900 /* Normalize server name */
1902 server_namec = silc_identifier_check(server_name, strlen(server_name),
1903 SILC_STRING_UTF8, 256, NULL);
1904 if (!server_namec) {
1905 silc_free(server_entry->server_name);
1906 silc_free(server_entry->server_info);
1907 silc_free(server_entry);
1912 silc_mutex_lock(conn->internal->lock);
1914 /* Add server to cache */
1915 if (!silc_idcache_add(conn->internal->server_cache, server_namec,
1916 &server_entry->id, server_entry)) {
1917 silc_free(server_namec);
1918 silc_free(server_entry->server_name);
1919 silc_free(server_entry->server_info);
1920 silc_free(server_entry);
1921 silc_mutex_unlock(conn->internal->lock);
1925 silc_mutex_unlock(conn->internal->lock);
1926 silc_client_ref_server(client, conn, server_entry);
1928 SILC_LOG_DEBUG(("Added %p", server_entry));
1930 return server_entry;
1933 /* Removes server from the cache by the server entry. */
1935 SilcBool silc_client_del_server(SilcClient client, SilcClientConnection conn,
1936 SilcServerEntry server)
1943 if (silc_atomic_sub_int8(&server->internal.refcnt, 1) > 0)
1946 SILC_LOG_DEBUG(("Deleting server %p", server));
1948 silc_mutex_lock(conn->internal->lock);
1949 ret = silc_idcache_del_by_context(conn->internal->server_cache,
1951 silc_mutex_unlock(conn->internal->lock);
1953 silc_free(server->server_name);
1954 silc_free(server->server_info);
1955 if (server->public_key)
1956 silc_pkcs_public_key_free(server->public_key);
1957 silc_atomic_uninit8(&server->internal.refcnt);
1958 silc_rwlock_free(server->internal.lock);
1964 /* Updates the `server_entry' with the new information sent as argument. */
1966 void silc_client_update_server(SilcClient client,
1967 SilcClientConnection conn,
1968 SilcServerEntry server_entry,
1969 const char *server_name,
1970 const char *server_info)
1972 char *server_namec = NULL;
1974 SILC_LOG_DEBUG(("Updating server %p", server_entry));
1977 (!server_entry->server_name ||
1978 !silc_utf8_strcasecmp(server_entry->server_name, server_name))) {
1980 server_namec = silc_identifier_check(server_name, strlen(server_name),
1981 SILC_STRING_UTF8, 256, NULL);
1985 silc_free(server_entry->server_name);
1986 server_entry->server_name = strdup(server_name);
1987 if (!server_entry->server_name)
1990 /* Update cache entry */
1991 silc_mutex_lock(conn->internal->lock);
1992 silc_idcache_update_by_context(conn->internal->server_cache, server_entry,
1993 NULL, server_namec, TRUE);
1994 silc_mutex_unlock(conn->internal->lock);
1998 (!server_entry->server_info ||
1999 !silc_utf8_strcasecmp(server_entry->server_info, server_info))) {
2000 silc_free(server_entry->server_info);
2001 server_entry->server_info = strdup(server_info);
2007 void silc_client_lock_server(SilcServerEntry server_entry)
2009 silc_rwlock_rdlock(server_entry->internal.lock);
2014 void silc_client_unlock_server(SilcServerEntry server_entry)
2016 silc_rwlock_unlock(server_entry->internal.lock);
2019 /* Take reference of server entry */
2021 SilcServerEntry silc_client_ref_server(SilcClient client,
2022 SilcClientConnection conn,
2023 SilcServerEntry server_entry)
2025 silc_atomic_add_int8(&server_entry->internal.refcnt, 1);
2026 SILC_LOG_DEBUG(("Server %p refcnt %d->%d", server_entry,
2027 silc_atomic_get_int8(&server_entry->internal.refcnt) - 1,
2028 silc_atomic_get_int8(&server_entry->internal.refcnt)));
2029 return server_entry;
2032 /* Release reference of server entry */
2034 void silc_client_unref_server(SilcClient client, SilcClientConnection conn,
2035 SilcServerEntry server_entry)
2038 SILC_LOG_DEBUG(("Server %p refcnt %d->%d", server_entry,
2039 silc_atomic_get_int8(&server_entry->internal.refcnt),
2040 silc_atomic_get_int8(&server_entry->internal.refcnt)
2042 silc_client_del_server(client, conn, server_entry);
2046 /* Free server entry list */
2048 void silc_client_list_free_servers(SilcClient client,
2049 SilcClientConnection conn,
2050 SilcDList server_list)
2052 SilcServerEntry server_entry;
2055 silc_dlist_start(server_list);
2056 while ((server_entry = silc_dlist_get(server_list)))
2057 silc_client_unref_server(client, conn, server_entry);
2059 silc_dlist_uninit(server_list);