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_ext(SilcClient client,
66 SilcClientConnection conn,
71 SilcIDCacheEntry id_cache;
74 SilcClientEntry entry;
75 char nick[128 + 1], *nicknamec, *parsed = NULL, *format = NULL;
77 if (!client || !conn || !nickname)
80 /* Get nickname from nickname@server string */
81 silc_parse_userfqdn(nickname, nick, sizeof(nick), NULL, 0);
83 /* Parse nickname in case it is formatted */
84 if (!silc_client_nickname_parse(client, conn, (char *)nick, &parsed))
87 if (!get_all && parsed)
88 format = (char *)nick;
90 parsed = silc_memdup(nick, strlen(nick));
95 SILC_LOG_DEBUG(("Find clients by nickname %s", parsed));
97 /* Normalize nickname for search */
98 nicknamec = silc_identifier_check(parsed, strlen(parsed),
99 SILC_STRING_UTF8, 128, NULL);
105 clients = silc_dlist_init();
107 silc_free(nicknamec);
112 silc_mutex_lock(conn->internal->lock);
114 /* Find from cache */
115 silc_list_init(list, struct SilcIDCacheEntryStruct, next);
116 if (!silc_idcache_find_by_name(conn->internal->client_cache, nicknamec,
118 silc_mutex_unlock(conn->internal->lock);
119 silc_free(nicknamec);
121 silc_dlist_uninit(clients);
124 silc_list_start(list);
126 if (!format && get_all) {
127 /* Take all without any further checking */
128 while ((id_cache = silc_list_get(list))) {
129 entry = id_cache->context;
130 if (!get_valid || entry->internal.valid) {
131 silc_client_ref_client(client, conn, id_cache->context);
132 silc_dlist_add(clients, id_cache->context);
136 /* Check multiple cache entries for exact match */
137 while ((id_cache = silc_list_get(list))) {
138 entry = id_cache->context;
139 if (silc_utf8_strcasecmp(entry->nickname,
140 format ? format : parsed) &&
141 (!get_valid || entry->internal.valid)) {
142 silc_client_ref_client(client, conn, entry);
143 silc_dlist_add(clients, entry);
145 /* If format is NULL, we find one exact match with the base
146 nickname (parsed). */
153 silc_mutex_unlock(conn->internal->lock);
155 silc_free(nicknamec);
158 if (!silc_dlist_count(clients)) {
159 silc_dlist_uninit(clients);
163 SILC_LOG_DEBUG(("Found %d clients", silc_dlist_count(clients)));
165 silc_dlist_start(clients);
169 /* Finds clients by nickname from local cache. */
171 SilcDList silc_client_get_clients_local(SilcClient client,
172 SilcClientConnection conn,
173 const char *nickname,
176 return silc_client_get_clients_local_ext(client, conn, nickname, return_all,
180 /********************** Client Resolving from Server ************************/
182 /* Resolving context */
185 SilcGetClientCallback completion;
187 SilcClientEntry client_entry;
188 } *SilcClientGetClientInternal;
190 /* Resolving command callback */
192 static SilcBool silc_client_get_clients_cb(SilcClient client,
193 SilcClientConnection conn,
200 SilcClientGetClientInternal i = context;
201 SilcClientEntry client_entry;
203 if (error != SILC_STATUS_OK) {
204 SILC_LOG_DEBUG(("Resolving failed: %s", silc_get_status_message(error)));
206 if (i->client_entry) {
207 i->client_entry->internal.resolve_cmd_ident = 0;
208 silc_client_unref_client(client, conn, i->client_entry);
212 i->completion(client, conn, error, NULL, i->context);
216 /* Add the returned client to list */
218 client_entry = va_arg(ap, SilcClientEntry);
219 silc_client_ref_client(client, conn, client_entry);
220 silc_dlist_add(i->clients, client_entry);
221 client_entry->internal.resolve_cmd_ident = 0;
224 if (status == SILC_STATUS_OK || status == SILC_STATUS_LIST_END) {
225 /* Deliver the clients to the caller */
227 SILC_LOG_DEBUG(("Resolved %d clients", silc_dlist_count(i->clients)));
229 if (i->client_entry) {
230 i->client_entry->internal.resolve_cmd_ident = 0;
231 silc_client_unref_client(client, conn, i->client_entry);
234 silc_dlist_start(i->clients);
235 i->completion(client, conn, SILC_STATUS_OK, i->clients, i->context);
243 silc_client_list_free(client, conn, i->clients);
248 /* Resolves client information from server by the client ID. */
251 silc_client_get_client_by_id_resolve(SilcClient client,
252 SilcClientConnection conn,
253 SilcClientID *client_id,
254 SilcBuffer attributes,
255 SilcGetClientCallback completion,
258 SilcClientGetClientInternal i;
259 SilcClientEntry client_entry;
261 SilcUInt16 cmd_ident;
263 if (!client || !conn | !client_id) {
264 SILC_LOG_ERROR(("Missing arguments to "
265 "silc_client_get_clients_by_id_resolve call"));
269 SILC_LOG_DEBUG(("Resolve client by ID (%s)",
270 silc_id_render(client_id, SILC_ID_CLIENT)));
272 i = silc_calloc(1, sizeof(*i));
275 i->completion = completion;
276 i->context = context;
277 i->clients = silc_dlist_init();
283 /* Attach to resolving, if on going */
284 client_entry = silc_client_get_client_by_id(client, conn, client_id);
285 if (client_entry && client_entry->internal.resolve_cmd_ident) {
286 SILC_LOG_DEBUG(("Attach to existing resolving"));
287 silc_client_unref_client(client, conn, client_entry);
288 silc_client_command_pending(conn, SILC_COMMAND_NONE,
289 client_entry->internal.resolve_cmd_ident,
290 silc_client_get_clients_cb, i);
291 return client_entry->internal.resolve_cmd_ident;
294 /* Send the command */
295 idp = silc_id_payload_encode(client_id, SILC_ID_CLIENT);
296 cmd_ident = silc_client_command_send(client, conn, SILC_COMMAND_WHOIS,
297 silc_client_get_clients_cb, i,
298 2, 3, silc_buffer_datalen(attributes),
299 4, silc_buffer_datalen(idp));
300 if (!cmd_ident && completion)
301 completion(client, conn, SILC_STATUS_ERR_RESOURCE_LIMIT, NULL, context);
303 if (client_entry && cmd_ident) {
304 client_entry->internal.resolve_cmd_ident = cmd_ident;
305 i->client_entry = client_entry;
307 silc_client_unref_client(client, conn, client_entry);
310 silc_buffer_free(idp);
315 /* Finds client entry or entries by the `nickname' and `server'. The
316 completion callback will be called when the client entries has been
317 found. Used internally by the library. */
319 static SilcUInt16 silc_client_get_clients_i(SilcClient client,
320 SilcClientConnection conn,
322 const char *nickname,
324 SilcBuffer attributes,
325 SilcGetClientCallback completion,
328 SilcClientGetClientInternal i;
329 char nick[128 + 1], serv[256 + 1], userhost[768 + 1], *parsed = NULL;
332 SILC_LOG_DEBUG(("Resolve client by %s command",
333 silc_get_command_name(command)));
335 if (!client || !conn) {
336 SILC_LOG_ERROR(("Missing arguments to silc_client_get_clients call"));
339 if (!nickname && !attributes) {
340 SILC_LOG_ERROR(("Missing arguments to silc_client_get_clients call"));
344 /* Parse server name from the nickname if set */
345 if (silc_parse_userfqdn(nickname, nick, sizeof(nick),
346 serv, sizeof(serv)) == 2)
347 server = (const char *)serv;
348 nickname = (const char *)nick;
350 /* Parse nickname in case it is formatted */
351 if (silc_client_nickname_parse(client, conn, (char *)nickname, &parsed))
352 nickname = (const char *)parsed;
354 i = silc_calloc(1, sizeof(*i));
359 i->clients = silc_dlist_init();
365 i->completion = completion;
366 i->context = context;
368 memset(userhost, 0, sizeof(userhost));
369 if (nickname && server) {
370 len = strlen(nickname) + strlen(server) + 3;
371 silc_strncat(userhost, len, nickname, strlen(nickname));
372 silc_strncat(userhost, len, "@", 1);
373 silc_strncat(userhost, len, server, strlen(server));
374 } else if (nickname) {
375 silc_strncat(userhost, sizeof(userhost) - 1, nickname, strlen(nickname));
379 /* Send the command */
380 if (command == SILC_COMMAND_IDENTIFY)
381 return silc_client_command_send(client, conn, SILC_COMMAND_IDENTIFY,
382 silc_client_get_clients_cb, i,
383 1, 1, userhost, strlen(userhost));
384 return silc_client_command_send(client, conn, SILC_COMMAND_WHOIS,
385 silc_client_get_clients_cb, i,
386 2, 1, userhost, strlen(userhost),
387 3, silc_buffer_datalen(attributes));
390 /* Get clients from server with IDENTIFY command */
392 SilcUInt16 silc_client_get_clients(SilcClient client,
393 SilcClientConnection conn,
394 const char *nickname,
396 SilcGetClientCallback completion,
399 return silc_client_get_clients_i(client, conn, SILC_COMMAND_IDENTIFY,
400 nickname, server, NULL,
401 completion, context);
404 /* Get clients from server with WHOIS command */
406 SilcUInt16 silc_client_get_clients_whois(SilcClient client,
407 SilcClientConnection conn,
408 const char *nickname,
410 SilcBuffer attributes,
411 SilcGetClientCallback completion,
414 return silc_client_get_clients_i(client, conn, SILC_COMMAND_WHOIS,
415 nickname, server, attributes,
416 completion, context);
419 /* ID list resolving context */
421 SilcGetClientCallback completion;
423 SilcBuffer client_id_list;
424 SilcUInt32 list_count;
425 } *GetClientsByListInternal;
427 static SilcBool silc_client_get_clients_list_cb(SilcClient client,
428 SilcClientConnection conn,
435 GetClientsByListInternal i = context;
436 SilcClientEntry client_entry;
442 /* Process the list after all replies have been received */
443 if (status != SILC_STATUS_OK && !SILC_STATUS_IS_ERROR(status) &&
444 status != SILC_STATUS_LIST_END)
447 SILC_LOG_DEBUG(("Resolved all clients"));
449 clients = silc_dlist_init();
451 status = SILC_STATUS_ERR_RESOURCE_LIMIT;
455 for (c = 0; c < i->list_count; c++) {
457 SILC_GET16_MSB(idp_len, i->client_id_list->data + 2);
459 if (!silc_id_payload_parse_id(i->client_id_list->data, idp_len, &id)) {
460 status = SILC_STATUS_ERR_BAD_CLIENT_ID;
464 /* Get client entry */
465 client_entry = silc_client_get_client_by_id(client, conn, &id.u.client_id);
467 silc_dlist_add(clients, client_entry);
469 if (!silc_buffer_pull(i->client_id_list, idp_len)) {
470 status = SILC_STATUS_ERR_BAD_CLIENT_ID;
475 silc_dlist_start(clients);
476 status = SILC_STATUS_OK;
478 i->completion(client, conn, status, clients, i->context);
481 if (status != SILC_STATUS_OK && i->completion)
482 i->completion(client, conn, status, NULL, i->context);
484 silc_client_list_free(client, conn, clients);
485 silc_buffer_free(i->client_id_list);
491 /* Gets client entries by the list of client ID's `client_id_list'. This
492 always resolves those client ID's it does not know yet from the server
493 so this function might take a while. The `client_id_list' is a list
494 of ID Payloads added one after other. JOIN command reply and USERS
495 command reply for example returns this sort of list. The `completion'
496 will be called after the entries are available. */
498 SilcUInt16 silc_client_get_clients_by_list(SilcClient client,
499 SilcClientConnection conn,
500 SilcUInt32 list_count,
501 SilcBuffer client_id_list,
502 SilcGetClientCallback completion,
505 GetClientsByListInternal in;
506 SilcClientEntry entry;
507 unsigned char **res_argv = NULL;
508 SilcUInt32 *res_argv_lens = NULL, *res_argv_types = NULL, res_argc = 0;
509 SilcUInt16 idp_len, cmd_ident;
514 SILC_LOG_DEBUG(("Resolve clients from Client ID list"));
516 if (!client || !conn || !client_id_list)
519 in = silc_calloc(1, sizeof(*in));
522 in->completion = completion;
523 in->context = context;
524 in->list_count = list_count;
525 in->client_id_list = silc_buffer_copy(client_id_list);
526 if (!in->client_id_list)
529 for (i = 0; i < list_count; i++) {
531 SILC_GET16_MSB(idp_len, client_id_list->data + 2);
533 if (!silc_id_payload_parse_id(client_id_list->data, idp_len, &id))
536 /* Check if we have this client cached already. If we don't have the
537 entry or it has incomplete info, then resolve it from the server. */
538 entry = silc_client_get_client_by_id(client, conn, &id.u.client_id);
539 if (!entry || !entry->nickname[0] || !entry->username[0] ||
542 res_argv = silc_calloc(list_count, sizeof(*res_argv));
543 res_argv_lens = silc_calloc(list_count, sizeof(*res_argv_lens));
544 res_argv_types = silc_calloc(list_count, sizeof(*res_argv_types));
545 if (!res_argv || !res_argv_lens || !res_argv_types) {
546 silc_client_unref_client(client, conn, entry);
551 res_argv[res_argc] = client_id_list->data;
552 res_argv_lens[res_argc] = idp_len;
553 res_argv_types[res_argc] = res_argc + 4;
556 silc_client_unref_client(client, conn, entry);
558 if (!silc_buffer_pull(client_id_list, idp_len))
561 silc_buffer_start(client_id_list);
563 /* Query the unknown client information from server */
565 cmd_ident = silc_client_command_send_argv(client,
566 conn, SILC_COMMAND_WHOIS,
567 silc_client_get_clients_list_cb,
568 in, res_argc, res_argv,
572 silc_free(res_argv_lens);
573 silc_free(res_argv_types);
577 /* We have the clients in cache, get them and call the completion */
578 silc_client_get_clients_list_cb(client, conn, SILC_COMMAND_WHOIS,
579 SILC_STATUS_OK, SILC_STATUS_OK, in, tmp);
583 silc_buffer_free(in->client_id_list);
586 silc_free(res_argv_lens);
587 silc_free(res_argv_types);
594 SilcClientConnection conn;
595 SilcChannelID channel_id;
596 SilcGetClientCallback completion;
599 } *GetClientsByChannelInternal;
601 SILC_CLIENT_CMD_FUNC(get_clients_by_channel_cb)
603 GetClientsByChannelInternal i = context;
604 SilcClientEntry *clients = NULL;
605 SilcUInt32 clients_count = 0;
606 SilcBool found = FALSE;
607 SilcChannelEntry channel;
608 SilcHashTableList htl;
617 channel = silc_client_get_channel_by_id(i->client, i->conn, &i->channel_id);
618 if (channel && !silc_hash_table_count(channel->user_list)) {
619 clients = silc_calloc(silc_hash_table_count(channel->user_list),
621 silc_hash_table_list(channel->user_list, &htl);
622 while (silc_hash_table_get(&htl, NULL, (void *)&chu))
623 clients[clients_count++] = chu->client;
624 silc_hash_table_list_reset(&htl);
629 i->completion(i->client, i->conn, clients, clients_count, i->context);
632 i->completion(i->client, i->conn, NULL, 0, i->context);
638 /* Gets client entries by the channel entry indicated by `channel'. Thus,
639 it resolves the clients currently on that channel. */
641 void silc_client_get_clients_by_channel(SilcClient client,
642 SilcClientConnection conn,
643 SilcChannelEntry channel,
644 SilcGetClientCallback completion,
647 GetClientsByChannelInternal in;
648 SilcHashTableList htl;
650 SilcClientEntry entry;
651 unsigned char **res_argv = NULL;
652 SilcUInt32 *res_argv_lens = NULL, *res_argv_types = NULL, res_argc = 0;
654 SilcBool wait_res = FALSE;
656 assert(client && conn && channel);
658 SILC_LOG_DEBUG(("Start"));
660 in = silc_calloc(1, sizeof(*in));
663 in->channel_id = *channel->id;
664 in->completion = completion;
665 in->context = context;
667 /* If user list does not exist, send USERS command. */
668 if (!channel->user_list || !silc_hash_table_count(channel->user_list)) {
669 SILC_LOG_DEBUG(("Sending USERS"));
670 silc_client_command_register(client, SILC_COMMAND_USERS, NULL, NULL,
671 silc_client_command_reply_users_i, 0,
673 silc_client_command_send(client, conn, SILC_COMMAND_USERS,
674 conn->cmd_ident, 1, 2, channel->channel_name,
675 strlen(channel->channel_name));
676 silc_client_command_pending(conn, SILC_COMMAND_USERS, conn->cmd_ident,
677 silc_client_command_get_clients_by_channel_cb,
682 silc_hash_table_list(channel->user_list, &htl);
683 while (silc_hash_table_get(&htl, NULL, (void *)&chu)) {
686 /* If the entry has incomplete info, then resolve it from the server. */
687 if (!entry->nickname[0] || !entry->realname) {
688 if (entry->status & SILC_CLIENT_STATUS_RESOLVING) {
689 /* Attach to this resolving and wait until it finishes */
690 silc_client_command_pending(
691 conn, SILC_COMMAND_NONE,
692 entry->resolve_cmd_ident,
693 silc_client_command_get_clients_by_channel_cb,
699 entry->status |= SILC_CLIENT_STATUS_RESOLVING;
700 entry->resolve_cmd_ident = conn->cmd_ident + 1;
702 idp = silc_id_payload_encode(entry->id, SILC_ID_CLIENT);
704 /* No we don't have it, query it from the server. Assemble argument
705 table that will be sent for the WHOIS command later. */
706 res_argv = silc_realloc(res_argv, sizeof(*res_argv) *
708 res_argv_lens = silc_realloc(res_argv_lens, sizeof(*res_argv_lens) *
710 res_argv_types = silc_realloc(res_argv_types, sizeof(*res_argv_types) *
712 res_argv[res_argc] = silc_memdup(idp->data, idp->len);
713 res_argv_lens[res_argc] = idp->len;
714 res_argv_types[res_argc] = res_argc + 4;
717 silc_buffer_free(idp);
720 silc_hash_table_list_reset(&htl);
722 /* Query the client information from server if the list included clients
723 that we don't know about. */
727 /* Send the WHOIS command to server */
728 res_cmd = silc_command_payload_encode(SILC_COMMAND_WHOIS,
729 res_argc, res_argv, res_argv_lens,
730 res_argv_types, ++conn->cmd_ident);
731 silc_client_packet_send(client, conn->sock, SILC_PACKET_COMMAND,
732 NULL, 0, NULL, NULL, res_cmd->data, res_cmd->len,
735 /* Register our own command reply for this command */
736 silc_client_command_register(client, SILC_COMMAND_WHOIS, NULL, NULL,
737 silc_client_command_reply_whois_i, 0,
740 /* Process the applications request after reply has been received */
741 silc_client_command_pending(
742 conn, SILC_COMMAND_WHOIS, conn->cmd_ident,
743 silc_client_command_get_clients_by_channel_cb,
747 silc_buffer_free(res_cmd);
749 silc_free(res_argv_lens);
750 silc_free(res_argv_types);
757 /* We have the clients in cache, get them and call the completion */
758 silc_client_command_get_clients_by_channel_cb((void *)in, NULL);
763 /************************** Client Entry Routines ***************************/
765 /* Creates new client entry and adds it to the ID cache. Returns pointer
768 SilcClientEntry silc_client_add_client(SilcClient client,
769 SilcClientConnection conn,
770 char *nickname, char *username,
771 char *userinfo, SilcClientID *id,
774 SilcClientEntry client_entry;
777 SILC_LOG_DEBUG(("Adding new client entry"));
779 /* Save the client infos */
780 client_entry = silc_calloc(1, sizeof(*client_entry));
784 silc_rwlock_alloc(&client_entry->internal.lock);
785 silc_atomic_init8(&client_entry->internal.refcnt, 0);
786 client_entry->id = *id;
787 client_entry->mode = mode;
788 client_entry->realname = userinfo ? strdup(userinfo) : NULL;
789 silc_parse_userfqdn(nickname, client_entry->nickname,
790 sizeof(client_entry->nickname),
791 client_entry->server,
792 sizeof(client_entry->server));
793 silc_parse_userfqdn(username, client_entry->username,
794 sizeof(client_entry->username),
795 client_entry->hostname,
796 sizeof(client_entry->hostname));
797 client_entry->channels = silc_hash_table_alloc(1, silc_hash_ptr, NULL, NULL,
798 NULL, NULL, NULL, TRUE);
799 if (!client_entry->channels) {
800 silc_free(client_entry->realname);
801 silc_free(client_entry);
805 /* Normalize nickname */
806 if (client_entry->nickname[0]) {
807 nick = silc_identifier_check(client_entry->nickname,
808 strlen(client_entry->nickname),
809 SILC_STRING_UTF8, 128, NULL);
811 silc_free(client_entry->realname);
812 silc_hash_table_free(client_entry->channels);
813 silc_free(client_entry);
818 silc_mutex_lock(conn->internal->lock);
820 /* Add client to cache, the normalized nickname is saved to cache */
821 if (!silc_idcache_add(conn->internal->client_cache, nick,
822 &client_entry->id, client_entry)) {
824 silc_free(client_entry->realname);
825 silc_hash_table_free(client_entry->channels);
826 silc_free(client_entry);
827 silc_mutex_unlock(conn->internal->lock);
831 client_entry->nickname_normalized = nick;
833 silc_mutex_unlock(conn->internal->lock);
834 silc_client_ref_client(client, conn, client_entry);
836 /* Format the nickname */
837 silc_client_nickname_format(client, conn, client_entry, FALSE);
839 if (client_entry->nickname[0])
840 client_entry->internal.valid = TRUE;
842 SILC_LOG_DEBUG(("Added %p", client_entry));
847 /* Updates the `client_entry' with the new information sent as argument.
848 This handles entry locking internally. */
850 void silc_client_update_client(SilcClient client,
851 SilcClientConnection conn,
852 SilcClientEntry client_entry,
853 const char *nickname,
854 const char *username,
855 const char *userinfo,
860 SILC_LOG_DEBUG(("Update client entry"));
862 silc_rwlock_wrlock(client_entry->internal.lock);
864 if (!client_entry->realname && userinfo)
865 client_entry->realname = strdup(userinfo);
866 if ((!client_entry->username[0] || !client_entry->hostname[0]) && username)
867 silc_parse_userfqdn(username, client_entry->username,
868 sizeof(client_entry->username),
869 client_entry->hostname,
870 sizeof(client_entry->username));
871 if (!client_entry->nickname[0] && nickname) {
872 silc_parse_userfqdn(nickname, client_entry->nickname,
873 sizeof(client_entry->nickname),
874 client_entry->server,
875 sizeof(client_entry->server));
877 /* Normalize nickname */
878 nick = silc_identifier_check(client_entry->nickname,
879 strlen(client_entry->nickname),
880 SILC_STRING_UTF8, 128, NULL);
882 silc_rwlock_unlock(client_entry->internal.lock);
886 /* Format nickname */
887 silc_client_nickname_format(client, conn, client_entry,
888 client_entry == conn->local_entry);
890 /* Update cache entry */
891 silc_mutex_lock(conn->internal->lock);
892 silc_idcache_update_by_context(conn->internal->client_cache,
893 client_entry, NULL, nick, TRUE);
894 silc_mutex_unlock(conn->internal->lock);
895 client_entry->nickname_normalized = nick;
896 client_entry->internal.valid = TRUE;
898 client_entry->mode = mode;
900 silc_rwlock_unlock(client_entry->internal.lock);
903 /* Change a client's nickname. Must be called with `client_entry' locked. */
905 SilcBool silc_client_change_nickname(SilcClient client,
906 SilcClientConnection conn,
907 SilcClientEntry client_entry,
908 const char *new_nick,
909 SilcClientID *new_id,
910 const unsigned char *idp,
915 SILC_LOG_DEBUG(("Change nickname %s to %s", client_entry->nickname,
918 /* Normalize nickname */
919 tmp = silc_identifier_check(new_nick, strlen(new_nick),
920 SILC_STRING_UTF8, 128, NULL);
924 /* Update the client entry */
925 silc_mutex_lock(conn->internal->lock);
926 if (!silc_idcache_update_by_context(conn->internal->client_cache,
927 client_entry, new_id, tmp, TRUE)) {
929 silc_mutex_unlock(conn->internal->lock);
932 silc_mutex_unlock(conn->internal->lock);
934 memset(client_entry->nickname, 0, sizeof(client_entry->nickname));
935 memcpy(client_entry->nickname, new_nick, strlen(new_nick));
936 client_entry->nickname_normalized = tmp;
937 silc_client_nickname_format(client, conn, client_entry,
938 client_entry == conn->local_entry);
940 /* For my client entry, update ID and set new ID to packet stream */
941 if (client_entry == conn->local_entry) {
942 if (idp && idp_len) {
943 silc_buffer_enlarge(conn->internal->local_idp, idp_len);
944 silc_buffer_put(conn->internal->local_idp, idp, idp_len);
947 silc_packet_set_ids(conn->stream, SILC_ID_CLIENT, conn->local_id,
951 client_entry->internal.valid = TRUE;
955 /* Deletes the client entry and frees all memory. */
957 void silc_client_del_client_entry(SilcClient client,
958 SilcClientConnection conn,
959 SilcClientEntry client_entry)
961 silc_free(client_entry->realname);
962 silc_free(client_entry->nickname_normalized);
963 silc_free(client_entry->internal.key);
964 if (client_entry->public_key)
965 silc_pkcs_public_key_free(client_entry->public_key);
966 silc_hash_table_free(client_entry->channels);
967 if (client_entry->internal.send_key)
968 silc_cipher_free(client_entry->internal.send_key);
969 if (client_entry->internal.receive_key)
970 silc_cipher_free(client_entry->internal.receive_key);
971 if (client_entry->internal.hmac_send)
972 silc_hmac_free(client_entry->internal.hmac_send);
973 if (client_entry->internal.hmac_receive)
974 silc_hmac_free(client_entry->internal.hmac_receive);
975 silc_client_ftp_session_free_client(client, client_entry);
976 if (client_entry->internal.ke)
977 silc_client_abort_key_agreement(client, conn, client_entry);
978 silc_atomic_uninit8(&client_entry->internal.refcnt);
979 silc_rwlock_free(client_entry->internal.lock);
980 silc_free(client_entry);
983 /* Removes client from the cache by the client entry. */
985 SilcBool silc_client_del_client(SilcClient client, SilcClientConnection conn,
986 SilcClientEntry client_entry)
993 if (silc_atomic_sub_int8(&client_entry->internal.refcnt, 1) > 0)
996 SILC_LOG_DEBUG(("Deleting client %p", client_entry));
998 silc_mutex_lock(conn->internal->lock);
999 ret = silc_idcache_del_by_context(conn->internal->client_cache,
1000 client_entry, NULL);
1001 silc_mutex_unlock(conn->internal->lock);
1004 /* Remove from channels */
1005 silc_client_remove_from_channels(client, conn, client_entry);
1007 /* Free the client entry data */
1008 silc_client_del_client_entry(client, conn, client_entry);
1014 /* Internal routine used to find client by ID and if not found this creates
1015 new client entry and returns it. */
1017 SilcClientEntry silc_client_get_client(SilcClient client,
1018 SilcClientConnection conn,
1019 SilcClientID *client_id)
1021 SilcClientEntry client_entry;
1023 client_entry = silc_client_get_client_by_id(client, conn, client_id);
1024 if (!client_entry) {
1025 client_entry = silc_client_add_client(client, conn, NULL, NULL, NULL,
1029 silc_client_ref_client(client, conn, client_entry);
1032 return client_entry;
1037 void silc_client_lock_client(SilcClientEntry client_entry)
1039 silc_rwlock_rdlock(client_entry->internal.lock);
1044 void silc_client_unlock_client(SilcClientEntry client_entry)
1046 silc_rwlock_unlock(client_entry->internal.lock);
1049 /* Take reference of client entry */
1051 SilcClientEntry silc_client_ref_client(SilcClient client,
1052 SilcClientConnection conn,
1053 SilcClientEntry client_entry)
1055 silc_atomic_add_int8(&client_entry->internal.refcnt, 1);
1056 SILC_LOG_DEBUG(("Client %p refcnt %d->%d", client_entry,
1057 silc_atomic_get_int8(&client_entry->internal.refcnt) - 1,
1058 silc_atomic_get_int8(&client_entry->internal.refcnt)));
1059 return client_entry;
1062 /* Release reference of client entry */
1064 void silc_client_unref_client(SilcClient client, SilcClientConnection conn,
1065 SilcClientEntry client_entry)
1068 SILC_LOG_DEBUG(("Client %p refcnt %d->%d", client_entry,
1069 silc_atomic_get_int8(&client_entry->internal.refcnt),
1070 silc_atomic_get_int8(&client_entry->internal.refcnt) - 1));
1071 silc_client_del_client(client, conn, client_entry);
1075 /* Free client entry list */
1077 void silc_client_list_free(SilcClient client, SilcClientConnection conn,
1078 SilcDList client_list)
1080 SilcClientEntry client_entry;
1083 silc_dlist_start(client_list);
1084 while ((client_entry = silc_dlist_get(client_list)))
1085 silc_client_unref_client(client, conn, client_entry);
1087 silc_dlist_uninit(client_list);
1091 /* Formats the nickname of the client specified by the `client_entry'.
1092 If the format is specified by the application this will format the
1093 nickname and replace the old nickname in the client entry. If the
1094 format string is not specified then this function has no effect.
1095 Returns the client entry that was formatted. */
1097 SilcClientEntry silc_client_nickname_format(SilcClient client,
1098 SilcClientConnection conn,
1099 SilcClientEntry client_entry,
1103 char newnick[128 + 1];
1104 int i, off = 0, len;
1106 SilcClientEntry entry, unformatted = NULL;
1107 SilcBool formatted = FALSE;
1109 if (!client->internal->params->nickname_format[0])
1110 return client_entry;
1111 if (!client_entry->nickname[0])
1114 SILC_LOG_DEBUG(("Format nickname"));
1116 /* Get all clients with same nickname. Do not perform the formatting
1117 if there aren't any clients with same nickname unless the application
1118 is forcing us to do so. */
1119 clients = silc_client_get_clients_local_ext(client, conn,
1120 client_entry->nickname,
1124 if (silc_dlist_count(clients) == 1 && !priority &&
1125 !client->internal->params->nickname_force_format) {
1126 silc_client_list_free(client, conn, clients);
1127 return client_entry;
1130 /* Is the requested client formatted already */
1131 if (client_entry->nickname_normalized &&
1132 !silc_utf8_strcasecmp(client_entry->nickname,
1133 client_entry->nickname_normalized))
1136 if (client->internal->params->nickname_force_format)
1139 /* Find unformatted client entry */
1140 while ((entry = silc_dlist_get(clients))) {
1141 if (!entry->internal.valid)
1143 if (entry == client_entry)
1145 if (silc_utf8_strcasecmp(entry->nickname, entry->nickname_normalized)) {
1146 unformatted = entry;
1151 /* If there are no other unformatted clients and the requested client is
1152 unformatted, just return it. */
1153 if (!unformatted && !formatted) {
1154 silc_client_list_free(client, conn, clients);
1155 return client_entry;
1158 /* If priority formatting then the requested client will get the
1159 unformatted nickname, and the unformatted client will get a new
1160 formatted nickname. */
1163 /* Simply change the client's nickname to unformatted */
1164 if (!silc_client_nickname_parse(client, conn, client_entry->nickname,
1168 silc_snprintf(client_entry->nickname, sizeof(client_entry->nickname),
1174 /* There was no other unformatted client */
1175 silc_client_list_free(client, conn, clients);
1176 return client_entry;
1179 /* Now format the previously unformatted client */
1180 client_entry = unformatted;
1184 /* If already formatted just return it */
1186 silc_client_list_free(client, conn, clients);
1187 return client_entry;
1190 memset(newnick, 0, sizeof(newnick));
1191 cp = client->internal->params->nickname_format;
1201 if (!client_entry->nickname[0])
1203 len = strlen(client_entry->nickname);
1204 memcpy(&newnick[off], client_entry->nickname, len);
1208 /* Stripped hostname */
1209 if (!client_entry->hostname[0])
1211 len = strcspn(client_entry->hostname, ".");
1212 i = strcspn(client_entry->hostname, "-");
1215 memcpy(&newnick[off], client_entry->hostname, len);
1220 if (!client_entry->hostname[0])
1222 len = strlen(client_entry->hostname);
1223 memcpy(&newnick[off], client_entry->hostname, len);
1227 /* Ascending number */
1232 if (silc_dlist_count(clients) == 1)
1235 silc_dlist_start(clients);
1236 while ((entry = silc_dlist_get(clients))) {
1237 if (!silc_utf8_strncasecmp(entry->nickname, newnick, off))
1239 if (strlen(entry->nickname) <= off)
1241 num = atoi(&entry->nickname[off]);
1246 memset(tmp, 0, sizeof(tmp));
1247 silc_snprintf(tmp, sizeof(tmp) - 1, "%d", ++max);
1249 memcpy(&newnick[off], tmp, len);
1254 /* Some other character in the string */
1255 memcpy(&newnick[off], cp, 1);
1264 memcpy(client_entry->nickname, newnick, strlen(newnick));
1265 silc_client_list_free(client, conn, clients);
1267 return client_entry;
1270 /* Parses nickname according to nickname format string */
1272 SilcBool silc_client_nickname_parse(SilcClient client,
1273 SilcClientConnection conn,
1277 char *cp, s = 0, e = 0, *nick;
1281 if (!client->internal->params->nickname_format[0]) {
1286 if (!nickname || !nickname[0])
1289 cp = client->internal->params->nickname_format;
1307 /* Get separator character */
1320 /* Parse the nickname */
1324 if (strchr(nickname, s))
1325 nick = strchr(nickname, s) + 1;
1327 if (strchr(nick, e))
1328 len = strchr(nick, e) - nick;
1332 *ret_nick = silc_memdup(nick, len);
1336 SILC_LOG_DEBUG(("Parsed nickname: %s", *ret_nick));
1341 /************************ Channel Searching Locally *************************/
1343 /* Finds entry for channel by the channel name. Returns the entry or NULL
1344 if the entry was not found. It is found only if the client is joined
1347 SilcChannelEntry silc_client_get_channel(SilcClient client,
1348 SilcClientConnection conn,
1351 SilcIDCacheEntry id_cache;
1352 SilcChannelEntry entry;
1354 if (!client || !conn || !channel)
1357 SILC_LOG_DEBUG(("Find channel %s", channel));
1359 /* Normalize name for search */
1360 channel = silc_channel_name_check(channel, strlen(channel), SILC_STRING_UTF8,
1365 silc_mutex_lock(conn->internal->lock);
1367 if (!silc_idcache_find_by_name_one(conn->internal->channel_cache, channel,
1369 silc_mutex_unlock(conn->internal->lock);
1374 SILC_LOG_DEBUG(("Found"));
1376 entry = id_cache->context;
1379 silc_client_ref_channel(client, conn, entry);
1380 silc_mutex_unlock(conn->internal->lock);
1387 /* Finds entry for channel by the channel ID. Returns the entry or NULL
1388 if the entry was not found. It is found only if the client is joined
1391 SilcChannelEntry silc_client_get_channel_by_id(SilcClient client,
1392 SilcClientConnection conn,
1393 SilcChannelID *channel_id)
1395 SilcIDCacheEntry id_cache;
1396 SilcChannelEntry entry;
1398 if (!client || !conn || !channel_id)
1401 SILC_LOG_DEBUG(("Find channel by id %s",
1402 silc_id_render(channel_id, SILC_ID_CHANNEL)));
1404 silc_mutex_lock(conn->internal->lock);
1406 if (!silc_idcache_find_by_id_one(conn->internal->channel_cache, channel_id,
1408 silc_mutex_unlock(conn->internal->lock);
1412 SILC_LOG_DEBUG(("Found"));
1414 entry = id_cache->context;
1417 silc_client_ref_channel(client, conn, entry);
1418 silc_mutex_unlock(conn->internal->lock);
1423 /********************** Channel Resolving from Server ***********************/
1425 /* Channel resolving context */
1428 SilcGetChannelCallback completion;
1430 } *SilcClientGetChannelInternal;
1432 /* Resolving command callback */
1434 static SilcBool silc_client_get_channel_cb(SilcClient client,
1435 SilcClientConnection conn,
1436 SilcCommand command,
1442 SilcClientGetChannelInternal i = context;
1443 SilcChannelEntry entry;
1445 if (error != SILC_STATUS_OK) {
1446 SILC_LOG_DEBUG(("Resolving failed: %s", silc_get_status_message(error)));
1448 i->completion(client, conn, error, NULL, i->context);
1452 /* Add the returned channel to list */
1453 if (i->completion) {
1454 entry = va_arg(ap, SilcChannelEntry);
1455 silc_client_ref_channel(client, conn, entry);
1456 silc_dlist_add(i->channels, entry);
1459 if (status == SILC_STATUS_OK || status == SILC_STATUS_LIST_END) {
1460 /* Deliver the channels to the caller */
1461 if (i->completion) {
1462 SILC_LOG_DEBUG(("Resolved %d channels", silc_dlist_count(i->channels)));
1463 silc_dlist_start(i->channels);
1464 i->completion(client, conn, SILC_STATUS_OK, i->channels, i->context);
1472 silc_client_list_free_channels(client, conn, i->channels);
1477 /* Resolves channel entry from the server by the channel name. */
1479 void silc_client_get_channel_resolve(SilcClient client,
1480 SilcClientConnection conn,
1482 SilcGetChannelCallback completion,
1485 SilcClientGetChannelInternal i;
1487 if (!client || !conn || !channel_name || !completion)
1490 SILC_LOG_DEBUG(("Resolve channel %s", channel_name));
1492 i = silc_calloc(1, sizeof(*i));
1495 i->completion = completion;
1496 i->context = context;
1497 i->channels = silc_dlist_init();
1503 /* Send the command */
1504 if (!silc_client_command_send(client, conn, SILC_COMMAND_IDENTIFY,
1505 silc_client_get_channel_cb, i, 1,
1506 3, channel_name, strlen(channel_name))) {
1508 completion(client, conn, SILC_STATUS_ERR_RESOURCE_LIMIT, NULL, context);
1512 /* Resolves channel information from the server by the channel ID. */
1515 silc_client_get_channel_by_id_resolve(SilcClient client,
1516 SilcClientConnection conn,
1517 SilcChannelID *channel_id,
1518 SilcGetChannelCallback completion,
1521 SilcClientGetChannelInternal i;
1523 SilcUInt16 cmd_ident;
1525 if (!client || !conn || !channel_id || !completion)
1528 SILC_LOG_DEBUG(("Resolve channel by id %s",
1529 silc_id_render(channel_id, SILC_ID_CHANNEL)));
1531 i = silc_calloc(1, sizeof(*i));
1534 i->completion = completion;
1535 i->context = context;
1536 i->channels = silc_dlist_init();
1542 /* Send the command */
1543 idp = silc_id_payload_encode(channel_id, SILC_ID_CHANNEL);
1544 cmd_ident = silc_client_command_send(client, conn, SILC_COMMAND_IDENTIFY,
1545 silc_client_get_channel_cb, i, 1,
1546 5, silc_buffer_datalen(idp));
1547 silc_buffer_free(idp);
1548 if (!cmd_ident && completion)
1549 completion(client, conn, SILC_STATUS_ERR_RESOURCE_LIMIT, NULL, context);
1554 /************************* Channel Entry Routines ***************************/
1556 /* Add new channel entry to the ID Cache */
1558 SilcChannelEntry silc_client_add_channel(SilcClient client,
1559 SilcClientConnection conn,
1560 const char *channel_name,
1562 SilcChannelID *channel_id)
1564 SilcChannelEntry channel;
1565 char *channel_namec;
1567 SILC_LOG_DEBUG(("Start"));
1569 channel = silc_calloc(1, sizeof(*channel));
1573 silc_rwlock_alloc(&channel->internal.lock);
1574 silc_atomic_init16(&channel->internal.refcnt, 0);
1575 channel->id = *channel_id;
1576 channel->mode = mode;
1578 channel->channel_name = strdup(channel_name);
1579 if (!channel->channel_name) {
1584 channel->user_list = silc_hash_table_alloc(1, silc_hash_ptr, NULL, NULL,
1585 NULL, NULL, NULL, TRUE);
1586 if (!channel->user_list) {
1587 silc_free(channel->channel_name);
1592 /* Normalize channel name */
1593 channel_namec = silc_channel_name_check(channel_name, strlen(channel_name),
1594 SILC_STRING_UTF8, 256, NULL);
1595 if (!channel_namec) {
1596 silc_free(channel->channel_name);
1597 silc_hash_table_free(channel->user_list);
1602 silc_mutex_lock(conn->internal->lock);
1604 /* Add channel to cache, the normalized channel name is saved to cache */
1605 if (!silc_idcache_add(conn->internal->channel_cache, channel_namec,
1606 &channel->id, channel)) {
1607 silc_free(channel_namec);
1608 silc_free(channel->channel_name);
1609 silc_hash_table_free(channel->user_list);
1611 silc_mutex_unlock(conn->internal->lock);
1615 silc_mutex_unlock(conn->internal->lock);
1616 silc_client_ref_channel(client, conn, channel);
1618 SILC_LOG_DEBUG(("Added %p", channel));
1623 /* Removes channel from the cache by the channel entry. */
1625 SilcBool silc_client_del_channel(SilcClient client, SilcClientConnection conn,
1626 SilcChannelEntry channel)
1628 SilcIDCacheEntry id_cache;
1629 SilcBool ret = TRUE;
1637 if (silc_atomic_sub_int16(&channel->internal.refcnt, 1) > 0)
1640 SILC_LOG_DEBUG(("Deleting channel %p", channel));
1642 silc_mutex_lock(conn->internal->lock);
1643 if (silc_idcache_find_by_context(conn->internal->channel_cache, channel,
1645 namec = id_cache->name;
1646 ret = silc_idcache_del_by_context(conn->internal->channel_cache,
1650 silc_mutex_unlock(conn->internal->lock);
1655 silc_client_empty_channel(client, conn, channel);
1656 silc_hash_table_free(channel->user_list);
1657 silc_free(channel->channel_name);
1658 silc_free(channel->topic);
1659 if (channel->founder_key)
1660 silc_pkcs_public_key_free(channel->founder_key);
1661 if (channel->internal.send_key)
1662 silc_cipher_free(channel->internal.send_key);
1663 if (channel->internal.receive_key)
1664 silc_cipher_free(channel->internal.receive_key);
1665 if (channel->internal.hmac)
1666 silc_hmac_free(channel->internal.hmac);
1667 if (channel->internal.old_channel_keys) {
1668 silc_dlist_start(channel->internal.old_channel_keys);
1669 while ((key = silc_dlist_get(channel->internal.old_channel_keys)))
1670 silc_cipher_free(key);
1671 silc_dlist_uninit(channel->internal.old_channel_keys);
1673 if (channel->internal.old_hmacs) {
1674 silc_dlist_start(channel->internal.old_hmacs);
1675 while ((hmac = silc_dlist_get(channel->internal.old_hmacs)))
1676 silc_hmac_free(hmac);
1677 silc_dlist_uninit(channel->internal.old_hmacs);
1679 if (channel->channel_pubkeys)
1680 silc_argument_list_free(channel->channel_pubkeys,
1681 SILC_ARGUMENT_PUBLIC_KEY);
1682 silc_client_del_channel_private_keys(client, conn, channel);
1683 silc_atomic_uninit16(&channel->internal.refcnt);
1684 silc_rwlock_free(channel->internal.lock);
1685 silc_schedule_task_del_by_context(conn->client->schedule, channel);
1691 /* Replaces the channel ID of the `channel' to `new_id'. Returns FALSE
1692 if the ID could not be changed. This handles entry locking internally. */
1694 SilcBool silc_client_replace_channel_id(SilcClient client,
1695 SilcClientConnection conn,
1696 SilcChannelEntry channel,
1697 SilcChannelID *new_id)
1699 SilcBool ret = FALSE;
1704 SILC_LOG_DEBUG(("Old Channel ID id(%s)",
1705 silc_id_render(&channel->id, SILC_ID_CHANNEL)));
1706 SILC_LOG_DEBUG(("New Channel ID id(%s)",
1707 silc_id_render(new_id, SILC_ID_CHANNEL)));
1710 silc_rwlock_wrlock(channel->internal.lock);
1711 silc_mutex_lock(conn->internal->lock);
1712 silc_idcache_update_by_context(conn->internal->channel_cache, channel,
1713 new_id, NULL, FALSE);
1714 silc_mutex_unlock(conn->internal->lock);
1715 silc_rwlock_unlock(channel->internal.lock);
1722 void silc_client_lock_channel(SilcChannelEntry channel_entry)
1724 silc_rwlock_rdlock(channel_entry->internal.lock);
1727 /* Unlock channel */
1729 void silc_client_unlock_channel(SilcChannelEntry channel_entry)
1731 silc_rwlock_unlock(channel_entry->internal.lock);
1734 /* Take reference of channel entry */
1736 SilcChannelEntry silc_client_ref_channel(SilcClient client,
1737 SilcClientConnection conn,
1738 SilcChannelEntry channel_entry)
1740 silc_atomic_add_int16(&channel_entry->internal.refcnt, 1);
1741 SILC_LOG_DEBUG(("Channel %p refcnt %d->%d", channel_entry,
1742 silc_atomic_get_int16(&channel_entry->internal.refcnt) - 1,
1743 silc_atomic_get_int16(&channel_entry->internal.refcnt)));
1744 return channel_entry;
1747 /* Release reference of channel entry */
1749 void silc_client_unref_channel(SilcClient client, SilcClientConnection conn,
1750 SilcChannelEntry channel_entry)
1752 if (channel_entry) {
1753 SILC_LOG_DEBUG(("Channel %p refcnt %d->%d", channel_entry,
1754 silc_atomic_get_int16(&channel_entry->internal.refcnt),
1755 silc_atomic_get_int16(&channel_entry->internal.refcnt)
1757 silc_client_del_channel(client, conn, channel_entry);
1761 /* Free channel entry list */
1763 void silc_client_list_free_channels(SilcClient client,
1764 SilcClientConnection conn,
1765 SilcDList channel_list)
1767 SilcChannelEntry channel_entry;
1770 silc_dlist_start(channel_list);
1771 while ((channel_entry = silc_dlist_get(channel_list)))
1772 silc_client_unref_channel(client, conn, channel_entry);
1774 silc_dlist_uninit(channel_list);
1778 /************************* Server Searching Locally *************************/
1780 /* Finds entry for server by the server name. */
1782 SilcServerEntry silc_client_get_server(SilcClient client,
1783 SilcClientConnection conn,
1786 SilcIDCacheEntry id_cache;
1787 SilcServerEntry entry;
1789 if (!client || !conn || !server_name)
1792 SILC_LOG_DEBUG(("Find server by name %s", server_name));
1794 /* Normalize server name for search */
1795 server_name = silc_identifier_check(server_name, strlen(server_name),
1796 SILC_STRING_UTF8, 256, NULL);
1800 silc_mutex_lock(conn->internal->lock);
1802 if (!silc_idcache_find_by_name_one(conn->internal->server_cache,
1803 server_name, &id_cache)) {
1804 silc_free(server_name);
1805 silc_mutex_unlock(conn->internal->lock);
1809 SILC_LOG_DEBUG(("Found"));
1812 entry = id_cache->context;
1813 silc_client_ref_server(client, conn, entry);
1815 silc_mutex_unlock(conn->internal->lock);
1817 silc_free(server_name);
1822 /* Finds entry for server by the server ID. */
1824 SilcServerEntry silc_client_get_server_by_id(SilcClient client,
1825 SilcClientConnection conn,
1826 SilcServerID *server_id)
1828 SilcIDCacheEntry id_cache;
1829 SilcServerEntry entry;
1831 if (!client || !conn || !server_id)
1834 SILC_LOG_DEBUG(("Find server by id %s",
1835 silc_id_render(server_id, SILC_ID_SERVER)));
1837 silc_mutex_lock(conn->internal->lock);
1839 if (!silc_idcache_find_by_id_one(conn->internal->server_cache,
1840 server_id, &id_cache)) {
1841 silc_mutex_unlock(conn->internal->lock);
1845 SILC_LOG_DEBUG(("Found"));
1848 entry = id_cache->context;
1849 silc_client_ref_server(client, conn, entry);
1851 silc_mutex_unlock(conn->internal->lock);
1856 /*********************** Server Resolving from Server ***********************/
1858 /* Resolving context */
1861 SilcGetServerCallback completion;
1863 } *SilcClientGetServerInternal;
1865 /* Resolving command callback */
1867 static SilcBool silc_client_get_server_cb(SilcClient client,
1868 SilcClientConnection conn,
1869 SilcCommand command,
1875 SilcClientGetServerInternal i = context;
1876 SilcServerEntry server;
1878 if (error != SILC_STATUS_OK) {
1879 SILC_LOG_DEBUG(("Resolving failed: %s", silc_get_status_message(error)));
1881 i->completion(client, conn, error, NULL, i->context);
1885 /* Add the returned servers to list */
1886 if (i->completion) {
1887 server = va_arg(ap, SilcServerEntry);
1888 silc_client_ref_server(client, conn, server);
1889 silc_dlist_add(i->servers, server);
1890 server->internal.resolve_cmd_ident = 0;
1893 if (status == SILC_STATUS_OK || status == SILC_STATUS_LIST_END) {
1894 /* Deliver the servers to the caller */
1895 if (i->completion) {
1896 SILC_LOG_DEBUG(("Resolved %d servers", silc_dlist_count(i->servers)));
1897 silc_dlist_start(i->servers);
1898 i->completion(client, conn, SILC_STATUS_OK, i->servers, i->context);
1906 silc_client_list_free_servers(client, conn, i->servers);
1911 /* Resolve server by server ID */
1914 silc_client_get_server_by_id_resolve(SilcClient client,
1915 SilcClientConnection conn,
1916 SilcServerID *server_id,
1917 SilcGetServerCallback completion,
1920 SilcClientGetServerInternal i;
1921 SilcServerEntry server;
1923 SilcUInt16 cmd_ident;
1925 if (!client || !conn || !server_id || !completion)
1928 SILC_LOG_DEBUG(("Resolve server by id %s",
1929 silc_id_render(server_id, SILC_ID_SERVER)));
1931 i = silc_calloc(1, sizeof(*i));
1934 i->completion = completion;
1935 i->context = context;
1936 i->servers = silc_dlist_init();
1942 /* Attach to resolving, if on going */
1943 server = silc_client_get_server_by_id(client, conn, server_id);
1944 if (server && server->internal.resolve_cmd_ident) {
1945 SILC_LOG_DEBUG(("Attach to existing resolving"));
1946 silc_client_unref_server(client, conn, server);
1947 silc_client_command_pending(conn, SILC_COMMAND_NONE,
1948 server->internal.resolve_cmd_ident,
1949 silc_client_get_server_cb, i);
1950 return server->internal.resolve_cmd_ident;
1953 /* Send the command */
1954 idp = silc_id_payload_encode(server_id, SILC_ID_SERVER);
1955 cmd_ident = silc_client_command_send(client, conn, SILC_COMMAND_IDENTIFY,
1956 silc_client_get_server_cb, i, 1,
1957 5, silc_buffer_datalen(idp));
1958 silc_buffer_free(idp);
1959 if (!cmd_ident && completion)
1960 completion(client, conn, SILC_STATUS_ERR_RESOURCE_LIMIT, NULL, context);
1962 if (server && cmd_ident)
1963 server->internal.resolve_cmd_ident = cmd_ident;
1965 silc_client_unref_server(client, conn, server);
1970 /************************** Server Entry Routines ***************************/
1972 /* Add new server entry */
1974 SilcServerEntry silc_client_add_server(SilcClient client,
1975 SilcClientConnection conn,
1976 const char *server_name,
1977 const char *server_info,
1978 SilcServerID *server_id)
1980 SilcServerEntry server_entry;
1981 char *server_namec = NULL;
1986 SILC_LOG_DEBUG(("Adding new server %s", server_name));
1988 server_entry = silc_calloc(1, sizeof(*server_entry));
1992 silc_rwlock_alloc(&server_entry->internal.lock);
1993 silc_atomic_init8(&server_entry->internal.refcnt, 0);
1994 server_entry->id = *server_id;
1996 server_entry->server_name = strdup(server_name);
1998 server_entry->server_info = strdup(server_info);
2000 /* Normalize server name */
2002 server_namec = silc_identifier_check(server_name, strlen(server_name),
2003 SILC_STRING_UTF8, 256, NULL);
2004 if (!server_namec) {
2005 silc_free(server_entry->server_name);
2006 silc_free(server_entry->server_info);
2007 silc_free(server_entry);
2012 silc_mutex_lock(conn->internal->lock);
2014 /* Add server to cache */
2015 if (!silc_idcache_add(conn->internal->server_cache, server_namec,
2016 &server_entry->id, server_entry)) {
2017 silc_free(server_namec);
2018 silc_free(server_entry->server_name);
2019 silc_free(server_entry->server_info);
2020 silc_free(server_entry);
2021 silc_mutex_unlock(conn->internal->lock);
2025 silc_mutex_unlock(conn->internal->lock);
2026 silc_client_ref_server(client, conn, server_entry);
2028 SILC_LOG_DEBUG(("Added %p", server_entry));
2030 return server_entry;
2033 /* Removes server from the cache by the server entry. */
2035 SilcBool silc_client_del_server(SilcClient client, SilcClientConnection conn,
2036 SilcServerEntry server)
2038 SilcIDCacheEntry id_cache;
2039 SilcBool ret = TRUE;
2045 if (silc_atomic_sub_int8(&server->internal.refcnt, 1) > 0)
2048 SILC_LOG_DEBUG(("Deleting server %p", server));
2050 silc_mutex_lock(conn->internal->lock);
2051 if (silc_idcache_find_by_context(conn->internal->server_cache, server,
2053 namec = id_cache->name;
2054 ret = silc_idcache_del_by_context(conn->internal->server_cache,
2058 silc_mutex_unlock(conn->internal->lock);
2060 silc_free(server->server_name);
2061 silc_free(server->server_info);
2062 if (server->public_key)
2063 silc_pkcs_public_key_free(server->public_key);
2064 silc_atomic_uninit8(&server->internal.refcnt);
2065 silc_rwlock_free(server->internal.lock);
2071 /* Updates the `server_entry' with the new information sent as argument. */
2073 void silc_client_update_server(SilcClient client,
2074 SilcClientConnection conn,
2075 SilcServerEntry server_entry,
2076 const char *server_name,
2077 const char *server_info)
2079 char *server_namec = NULL;
2081 SILC_LOG_DEBUG(("Updating server %p", server_entry));
2084 (!server_entry->server_name ||
2085 !silc_utf8_strcasecmp(server_entry->server_name, server_name))) {
2087 server_namec = silc_identifier_check(server_name, strlen(server_name),
2088 SILC_STRING_UTF8, 256, NULL);
2092 silc_free(server_entry->server_name);
2093 server_entry->server_name = strdup(server_name);
2094 if (!server_entry->server_name)
2097 /* Update cache entry */
2098 silc_mutex_lock(conn->internal->lock);
2099 silc_idcache_update_by_context(conn->internal->server_cache, server_entry,
2100 NULL, server_namec, TRUE);
2101 silc_mutex_unlock(conn->internal->lock);
2105 (!server_entry->server_info ||
2106 !silc_utf8_strcasecmp(server_entry->server_info, server_info))) {
2107 silc_free(server_entry->server_info);
2108 server_entry->server_info = strdup(server_info);
2114 void silc_client_lock_server(SilcServerEntry server_entry)
2116 silc_rwlock_rdlock(server_entry->internal.lock);
2121 void silc_client_unlock_server(SilcServerEntry server_entry)
2123 silc_rwlock_unlock(server_entry->internal.lock);
2126 /* Take reference of server entry */
2128 SilcServerEntry silc_client_ref_server(SilcClient client,
2129 SilcClientConnection conn,
2130 SilcServerEntry server_entry)
2132 silc_atomic_add_int8(&server_entry->internal.refcnt, 1);
2133 SILC_LOG_DEBUG(("Server %p refcnt %d->%d", server_entry,
2134 silc_atomic_get_int8(&server_entry->internal.refcnt) - 1,
2135 silc_atomic_get_int8(&server_entry->internal.refcnt)));
2136 return server_entry;
2139 /* Release reference of server entry */
2141 void silc_client_unref_server(SilcClient client, SilcClientConnection conn,
2142 SilcServerEntry server_entry)
2145 SILC_LOG_DEBUG(("Server %p refcnt %d->%d", server_entry,
2146 silc_atomic_get_int8(&server_entry->internal.refcnt),
2147 silc_atomic_get_int8(&server_entry->internal.refcnt)
2149 silc_client_del_server(client, conn, server_entry);
2153 /* Free server entry list */
2155 void silc_client_list_free_servers(SilcClient client,
2156 SilcClientConnection conn,
2157 SilcDList server_list)
2159 SilcServerEntry server_entry;
2162 silc_dlist_start(server_list);
2163 while ((server_entry = silc_dlist_get(server_list)))
2164 silc_client_unref_server(client, conn, server_entry);
2166 silc_dlist_uninit(server_list);