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 *nicknamec, *parsed = NULL, *format = NULL;
77 if (!client || !conn || !nickname)
80 /* Parse nickname in case it is formatted */
81 if (!silc_client_nickname_parse(client, conn, (char *)nickname, &parsed))
84 if (!get_all && parsed)
85 format = (char *)nickname;
87 parsed = silc_memdup(nickname, strlen(nickname));
92 SILC_LOG_DEBUG(("Find clients by nickname %s", parsed));
94 /* Normalize nickname for search */
95 nicknamec = silc_identifier_check(parsed, strlen(parsed),
96 SILC_STRING_UTF8, 128, NULL);
102 clients = silc_dlist_init();
104 silc_free(nicknamec);
109 silc_mutex_lock(conn->internal->lock);
111 /* Find from cache */
112 silc_list_init(list, struct SilcIDCacheEntryStruct, next);
113 if (!silc_idcache_find_by_name(conn->internal->client_cache, nicknamec,
115 silc_mutex_unlock(conn->internal->lock);
116 silc_free(nicknamec);
118 silc_dlist_uninit(clients);
121 silc_list_start(list);
123 if (!format && get_all) {
124 /* Take all without any further checking */
125 while ((id_cache = silc_list_get(list))) {
126 entry = id_cache->context;
127 if (!get_valid || entry->internal.valid) {
128 silc_client_ref_client(client, conn, id_cache->context);
129 silc_dlist_add(clients, id_cache->context);
133 /* Check multiple cache entries for exact match */
134 while ((id_cache = silc_list_get(list))) {
135 entry = id_cache->context;
136 if (silc_utf8_strcasecmp(entry->nickname,
137 format ? format : parsed) &&
138 (!get_valid || entry->internal.valid)) {
139 silc_client_ref_client(client, conn, entry);
140 silc_dlist_add(clients, entry);
142 /* If format is NULL, we find one exact match with the base
143 nickname (parsed). */
150 silc_mutex_unlock(conn->internal->lock);
152 silc_free(nicknamec);
155 if (!silc_dlist_count(clients)) {
156 silc_dlist_uninit(clients);
160 SILC_LOG_DEBUG(("Found %d clients", silc_dlist_count(clients)));
162 silc_dlist_start(clients);
166 /* Finds clients by nickname from local cache. */
168 SilcDList silc_client_get_clients_local(SilcClient client,
169 SilcClientConnection conn,
170 const char *nickname,
173 return silc_client_get_clients_local_ext(client, conn, nickname, return_all,
177 /********************** Client Resolving from Server ************************/
179 /* Resolving context */
182 SilcGetClientCallback completion;
184 SilcClientEntry client_entry;
185 } *SilcClientGetClientInternal;
187 /* Resolving command callback */
189 static SilcBool silc_client_get_clients_cb(SilcClient client,
190 SilcClientConnection conn,
197 SilcClientGetClientInternal i = context;
198 SilcClientEntry client_entry;
200 if (error != SILC_STATUS_OK) {
201 SILC_LOG_DEBUG(("Resolving failed: %s", silc_get_status_message(error)));
203 if (i->client_entry) {
204 i->client_entry->internal.resolve_cmd_ident = 0;
205 silc_client_unref_client(client, conn, i->client_entry);
209 i->completion(client, conn, error, NULL, i->context);
213 /* Add the returned client to list */
215 client_entry = va_arg(ap, SilcClientEntry);
216 silc_client_ref_client(client, conn, client_entry);
217 silc_dlist_add(i->clients, client_entry);
218 client_entry->internal.resolve_cmd_ident = 0;
221 if (status == SILC_STATUS_OK || status == SILC_STATUS_LIST_END) {
222 /* Deliver the clients to the caller */
224 SILC_LOG_DEBUG(("Resolved %d clients", silc_dlist_count(i->clients)));
226 if (i->client_entry) {
227 i->client_entry->internal.resolve_cmd_ident = 0;
228 silc_client_unref_client(client, conn, i->client_entry);
231 silc_dlist_start(i->clients);
232 i->completion(client, conn, SILC_STATUS_OK, i->clients, i->context);
240 silc_client_list_free(client, conn, i->clients);
245 /* Resolves client information from server by the client ID. */
248 silc_client_get_client_by_id_resolve(SilcClient client,
249 SilcClientConnection conn,
250 SilcClientID *client_id,
251 SilcBuffer attributes,
252 SilcGetClientCallback completion,
255 SilcClientGetClientInternal i;
256 SilcClientEntry client_entry;
258 SilcUInt16 cmd_ident;
260 if (!client || !conn | !client_id) {
261 SILC_LOG_ERROR(("Missing arguments to "
262 "silc_client_get_clients_by_id_resolve call"));
266 SILC_LOG_DEBUG(("Resolve client by ID (%s)",
267 silc_id_render(client_id, SILC_ID_CLIENT)));
269 i = silc_calloc(1, sizeof(*i));
272 i->completion = completion;
273 i->context = context;
274 i->clients = silc_dlist_init();
280 /* Attach to resolving, if on going */
281 client_entry = silc_client_get_client_by_id(client, conn, client_id);
282 if (client_entry && client_entry->internal.resolve_cmd_ident) {
283 SILC_LOG_DEBUG(("Attach to existing resolving"));
284 silc_client_unref_client(client, conn, client_entry);
285 silc_client_command_pending(conn, SILC_COMMAND_NONE,
286 client_entry->internal.resolve_cmd_ident,
287 silc_client_get_clients_cb, i);
288 return client_entry->internal.resolve_cmd_ident;
291 /* Send the command */
292 idp = silc_id_payload_encode(client_id, SILC_ID_CLIENT);
293 cmd_ident = silc_client_command_send(client, conn, SILC_COMMAND_WHOIS,
294 silc_client_get_clients_cb, i,
295 2, 3, silc_buffer_datalen(attributes),
296 4, silc_buffer_datalen(idp));
297 if (!cmd_ident && completion)
298 completion(client, conn, SILC_STATUS_ERR_RESOURCE_LIMIT, NULL, context);
300 if (client_entry && cmd_ident) {
301 client_entry->internal.resolve_cmd_ident = cmd_ident;
302 i->client_entry = client_entry;
304 silc_client_unref_client(client, conn, client_entry);
307 silc_buffer_free(idp);
312 /* Finds client entry or entries by the `nickname' and `server'. The
313 completion callback will be called when the client entries has been
314 found. Used internally by the library. */
316 static SilcUInt16 silc_client_get_clients_i(SilcClient client,
317 SilcClientConnection conn,
319 const char *nickname,
321 SilcBuffer attributes,
322 SilcGetClientCallback completion,
325 SilcClientGetClientInternal i;
326 char nick[128 + 1], serv[256 + 1], userhost[768 + 1], *parsed = NULL;
329 SILC_LOG_DEBUG(("Resolve client by %s command",
330 silc_get_command_name(command)));
332 if (!client || !conn) {
333 SILC_LOG_ERROR(("Missing arguments to silc_client_get_clients call"));
336 if (!nickname && !attributes) {
337 SILC_LOG_ERROR(("Missing arguments to silc_client_get_clients call"));
341 /* Parse server name from the nickname if set */
342 if (silc_parse_userfqdn(nickname, nick, sizeof(nick),
343 serv, sizeof(serv) == 2))
344 server = (const char *)serv;
345 nickname = (const char *)nick;
347 /* Parse nickname in case it is formatted */
348 if (silc_client_nickname_parse(client, conn, (char *)nickname, &parsed))
349 nickname = (const char *)parsed;
351 i = silc_calloc(1, sizeof(*i));
356 i->clients = silc_dlist_init();
362 i->completion = completion;
363 i->context = context;
365 memset(userhost, 0, sizeof(userhost));
366 if (nickname && server) {
367 len = strlen(nickname) + strlen(server) + 3;
368 silc_strncat(userhost, len, nickname, strlen(nickname));
369 silc_strncat(userhost, len, "@", 1);
370 silc_strncat(userhost, len, server, strlen(server));
371 } else if (nickname) {
372 silc_strncat(userhost, sizeof(userhost) - 1, nickname, strlen(nickname));
376 /* Send the command */
377 if (command == SILC_COMMAND_IDENTIFY)
378 return silc_client_command_send(client, conn, SILC_COMMAND_IDENTIFY,
379 silc_client_get_clients_cb, i,
380 1, 1, userhost, strlen(userhost));
381 return silc_client_command_send(client, conn, SILC_COMMAND_WHOIS,
382 silc_client_get_clients_cb, i,
383 2, 1, userhost, strlen(userhost),
384 3, silc_buffer_datalen(attributes));
387 /* Get clients from server with IDENTIFY command */
389 SilcUInt16 silc_client_get_clients(SilcClient client,
390 SilcClientConnection conn,
391 const char *nickname,
393 SilcGetClientCallback completion,
396 return silc_client_get_clients_i(client, conn, SILC_COMMAND_IDENTIFY,
397 nickname, server, NULL,
398 completion, context);
401 /* Get clients from server with WHOIS command */
403 SilcUInt16 silc_client_get_clients_whois(SilcClient client,
404 SilcClientConnection conn,
405 const char *nickname,
407 SilcBuffer attributes,
408 SilcGetClientCallback completion,
411 return silc_client_get_clients_i(client, conn, SILC_COMMAND_WHOIS,
412 nickname, server, attributes,
413 completion, context);
416 /* ID list resolving context */
418 SilcGetClientCallback completion;
420 SilcBuffer client_id_list;
421 SilcUInt32 list_count;
422 } *GetClientsByListInternal;
424 static SilcBool silc_client_get_clients_list_cb(SilcClient client,
425 SilcClientConnection conn,
432 GetClientsByListInternal i = context;
433 SilcClientEntry client_entry;
439 /* Process the list after all replies have been received */
440 if (status != SILC_STATUS_OK && !SILC_STATUS_IS_ERROR(status) &&
441 status != SILC_STATUS_LIST_END)
444 SILC_LOG_DEBUG(("Resolved all clients"));
446 clients = silc_dlist_init();
448 status = SILC_STATUS_ERR_RESOURCE_LIMIT;
452 for (c = 0; c < i->list_count; c++) {
454 SILC_GET16_MSB(idp_len, i->client_id_list->data + 2);
456 if (!silc_id_payload_parse_id(i->client_id_list->data, idp_len, &id)) {
457 status = SILC_STATUS_ERR_BAD_CLIENT_ID;
461 /* Get client entry */
462 client_entry = silc_client_get_client_by_id(client, conn, &id.u.client_id);
464 silc_dlist_add(clients, client_entry);
466 if (!silc_buffer_pull(i->client_id_list, idp_len)) {
467 status = SILC_STATUS_ERR_BAD_CLIENT_ID;
472 silc_dlist_start(clients);
473 status = SILC_STATUS_OK;
475 i->completion(client, conn, status, clients, i->context);
478 if (status != SILC_STATUS_OK && i->completion)
479 i->completion(client, conn, status, NULL, i->context);
481 silc_client_list_free(client, conn, clients);
482 silc_buffer_free(i->client_id_list);
488 /* Gets client entries by the list of client ID's `client_id_list'. This
489 always resolves those client ID's it does not know yet from the server
490 so this function might take a while. The `client_id_list' is a list
491 of ID Payloads added one after other. JOIN command reply and USERS
492 command reply for example returns this sort of list. The `completion'
493 will be called after the entries are available. */
495 SilcUInt16 silc_client_get_clients_by_list(SilcClient client,
496 SilcClientConnection conn,
497 SilcUInt32 list_count,
498 SilcBuffer client_id_list,
499 SilcGetClientCallback completion,
502 GetClientsByListInternal in;
503 SilcClientEntry entry;
504 unsigned char **res_argv = NULL;
505 SilcUInt32 *res_argv_lens = NULL, *res_argv_types = NULL, res_argc = 0;
506 SilcUInt16 idp_len, cmd_ident;
511 SILC_LOG_DEBUG(("Resolve clients from Client ID list"));
513 if (!client || !conn || !client_id_list)
516 in = silc_calloc(1, sizeof(*in));
519 in->completion = completion;
520 in->context = context;
521 in->list_count = list_count;
522 in->client_id_list = silc_buffer_copy(client_id_list);
523 if (!in->client_id_list)
526 for (i = 0; i < list_count; i++) {
528 SILC_GET16_MSB(idp_len, client_id_list->data + 2);
530 if (!silc_id_payload_parse_id(client_id_list->data, idp_len, &id))
533 /* Check if we have this client cached already. If we don't have the
534 entry or it has incomplete info, then resolve it from the server. */
535 entry = silc_client_get_client_by_id(client, conn, &id.u.client_id);
536 if (!entry || !entry->nickname[0] || !entry->username[0] ||
539 res_argv = silc_calloc(list_count, sizeof(*res_argv));
540 res_argv_lens = silc_calloc(list_count, sizeof(*res_argv_lens));
541 res_argv_types = silc_calloc(list_count, sizeof(*res_argv_types));
542 if (!res_argv || !res_argv_lens || !res_argv_types) {
543 silc_client_unref_client(client, conn, entry);
548 res_argv[res_argc] = client_id_list->data;
549 res_argv_lens[res_argc] = idp_len;
550 res_argv_types[res_argc] = res_argc + 4;
553 silc_client_unref_client(client, conn, entry);
555 if (!silc_buffer_pull(client_id_list, idp_len))
558 silc_buffer_start(client_id_list);
560 /* Query the unknown client information from server */
562 cmd_ident = silc_client_command_send_argv(client,
563 conn, SILC_COMMAND_WHOIS,
564 silc_client_get_clients_list_cb,
565 in, res_argc, res_argv,
569 silc_free(res_argv_lens);
570 silc_free(res_argv_types);
574 /* We have the clients in cache, get them and call the completion */
575 silc_client_get_clients_list_cb(client, conn, SILC_COMMAND_WHOIS,
576 SILC_STATUS_OK, SILC_STATUS_OK, in, tmp);
580 silc_buffer_free(in->client_id_list);
583 silc_free(res_argv_lens);
584 silc_free(res_argv_types);
591 SilcClientConnection conn;
592 SilcChannelID channel_id;
593 SilcGetClientCallback completion;
596 } *GetClientsByChannelInternal;
598 SILC_CLIENT_CMD_FUNC(get_clients_by_channel_cb)
600 GetClientsByChannelInternal i = context;
601 SilcClientEntry *clients = NULL;
602 SilcUInt32 clients_count = 0;
603 SilcBool found = FALSE;
604 SilcChannelEntry channel;
605 SilcHashTableList htl;
614 channel = silc_client_get_channel_by_id(i->client, i->conn, &i->channel_id);
615 if (channel && !silc_hash_table_count(channel->user_list)) {
616 clients = silc_calloc(silc_hash_table_count(channel->user_list),
618 silc_hash_table_list(channel->user_list, &htl);
619 while (silc_hash_table_get(&htl, NULL, (void *)&chu))
620 clients[clients_count++] = chu->client;
621 silc_hash_table_list_reset(&htl);
626 i->completion(i->client, i->conn, clients, clients_count, i->context);
629 i->completion(i->client, i->conn, NULL, 0, i->context);
635 /* Gets client entries by the channel entry indicated by `channel'. Thus,
636 it resolves the clients currently on that channel. */
638 void silc_client_get_clients_by_channel(SilcClient client,
639 SilcClientConnection conn,
640 SilcChannelEntry channel,
641 SilcGetClientCallback completion,
644 GetClientsByChannelInternal in;
645 SilcHashTableList htl;
647 SilcClientEntry entry;
648 unsigned char **res_argv = NULL;
649 SilcUInt32 *res_argv_lens = NULL, *res_argv_types = NULL, res_argc = 0;
651 SilcBool wait_res = FALSE;
653 assert(client && conn && channel);
655 SILC_LOG_DEBUG(("Start"));
657 in = silc_calloc(1, sizeof(*in));
660 in->channel_id = *channel->id;
661 in->completion = completion;
662 in->context = context;
664 /* If user list does not exist, send USERS command. */
665 if (!channel->user_list || !silc_hash_table_count(channel->user_list)) {
666 SILC_LOG_DEBUG(("Sending USERS"));
667 silc_client_command_register(client, SILC_COMMAND_USERS, NULL, NULL,
668 silc_client_command_reply_users_i, 0,
670 silc_client_command_send(client, conn, SILC_COMMAND_USERS,
671 conn->cmd_ident, 1, 2, channel->channel_name,
672 strlen(channel->channel_name));
673 silc_client_command_pending(conn, SILC_COMMAND_USERS, conn->cmd_ident,
674 silc_client_command_get_clients_by_channel_cb,
679 silc_hash_table_list(channel->user_list, &htl);
680 while (silc_hash_table_get(&htl, NULL, (void *)&chu)) {
683 /* If the entry has incomplete info, then resolve it from the server. */
684 if (!entry->nickname[0] || !entry->realname) {
685 if (entry->status & SILC_CLIENT_STATUS_RESOLVING) {
686 /* Attach to this resolving and wait until it finishes */
687 silc_client_command_pending(
688 conn, SILC_COMMAND_NONE,
689 entry->resolve_cmd_ident,
690 silc_client_command_get_clients_by_channel_cb,
696 entry->status |= SILC_CLIENT_STATUS_RESOLVING;
697 entry->resolve_cmd_ident = conn->cmd_ident + 1;
699 idp = silc_id_payload_encode(entry->id, SILC_ID_CLIENT);
701 /* No we don't have it, query it from the server. Assemble argument
702 table that will be sent for the WHOIS command later. */
703 res_argv = silc_realloc(res_argv, sizeof(*res_argv) *
705 res_argv_lens = silc_realloc(res_argv_lens, sizeof(*res_argv_lens) *
707 res_argv_types = silc_realloc(res_argv_types, sizeof(*res_argv_types) *
709 res_argv[res_argc] = silc_memdup(idp->data, idp->len);
710 res_argv_lens[res_argc] = idp->len;
711 res_argv_types[res_argc] = res_argc + 4;
714 silc_buffer_free(idp);
717 silc_hash_table_list_reset(&htl);
719 /* Query the client information from server if the list included clients
720 that we don't know about. */
724 /* Send the WHOIS command to server */
725 res_cmd = silc_command_payload_encode(SILC_COMMAND_WHOIS,
726 res_argc, res_argv, res_argv_lens,
727 res_argv_types, ++conn->cmd_ident);
728 silc_client_packet_send(client, conn->sock, SILC_PACKET_COMMAND,
729 NULL, 0, NULL, NULL, res_cmd->data, res_cmd->len,
732 /* Register our own command reply for this command */
733 silc_client_command_register(client, SILC_COMMAND_WHOIS, NULL, NULL,
734 silc_client_command_reply_whois_i, 0,
737 /* Process the applications request after reply has been received */
738 silc_client_command_pending(
739 conn, SILC_COMMAND_WHOIS, conn->cmd_ident,
740 silc_client_command_get_clients_by_channel_cb,
744 silc_buffer_free(res_cmd);
746 silc_free(res_argv_lens);
747 silc_free(res_argv_types);
754 /* We have the clients in cache, get them and call the completion */
755 silc_client_command_get_clients_by_channel_cb((void *)in, NULL);
760 /************************** Client Entry Routines ***************************/
762 /* Creates new client entry and adds it to the ID cache. Returns pointer
765 SilcClientEntry silc_client_add_client(SilcClient client,
766 SilcClientConnection conn,
767 char *nickname, char *username,
768 char *userinfo, SilcClientID *id,
771 SilcClientEntry client_entry;
774 SILC_LOG_DEBUG(("Adding new client entry"));
776 /* Save the client infos */
777 client_entry = silc_calloc(1, sizeof(*client_entry));
781 silc_rwlock_alloc(&client_entry->internal.lock);
782 silc_atomic_init8(&client_entry->internal.refcnt, 0);
783 client_entry->id = *id;
784 client_entry->mode = mode;
785 client_entry->realname = userinfo ? strdup(userinfo) : NULL;
786 silc_parse_userfqdn(nickname, client_entry->nickname,
787 sizeof(client_entry->nickname),
788 client_entry->server,
789 sizeof(client_entry->server));
790 silc_parse_userfqdn(username, client_entry->username,
791 sizeof(client_entry->username),
792 client_entry->hostname,
793 sizeof(client_entry->hostname));
794 client_entry->channels = silc_hash_table_alloc(1, silc_hash_ptr, NULL, NULL,
795 NULL, NULL, NULL, TRUE);
796 if (!client_entry->channels) {
797 silc_free(client_entry->realname);
798 silc_free(client_entry);
802 /* Normalize nickname */
803 if (client_entry->nickname[0]) {
804 nick = silc_identifier_check(client_entry->nickname,
805 strlen(client_entry->nickname),
806 SILC_STRING_UTF8, 128, NULL);
808 silc_free(client_entry->realname);
809 silc_hash_table_free(client_entry->channels);
810 silc_free(client_entry);
815 silc_mutex_lock(conn->internal->lock);
817 /* Add client to cache, the normalized nickname is saved to cache */
818 if (!silc_idcache_add(conn->internal->client_cache, nick,
819 &client_entry->id, client_entry)) {
821 silc_free(client_entry->realname);
822 silc_hash_table_free(client_entry->channels);
823 silc_free(client_entry);
824 silc_mutex_unlock(conn->internal->lock);
828 client_entry->nickname_normalized = nick;
830 silc_mutex_unlock(conn->internal->lock);
831 silc_client_ref_client(client, conn, client_entry);
833 /* Format the nickname */
834 silc_client_nickname_format(client, conn, client_entry, FALSE);
836 if (client_entry->nickname[0])
837 client_entry->internal.valid = TRUE;
839 SILC_LOG_DEBUG(("Added %p", client_entry));
844 /* Updates the `client_entry' with the new information sent as argument.
845 This handles entry locking internally. */
847 void silc_client_update_client(SilcClient client,
848 SilcClientConnection conn,
849 SilcClientEntry client_entry,
850 const char *nickname,
851 const char *username,
852 const char *userinfo,
857 SILC_LOG_DEBUG(("Update client entry"));
859 silc_rwlock_wrlock(client_entry->internal.lock);
861 if (!client_entry->realname && userinfo)
862 client_entry->realname = strdup(userinfo);
863 if ((!client_entry->username[0] || !client_entry->hostname[0]) && username)
864 silc_parse_userfqdn(username, client_entry->username,
865 sizeof(client_entry->username),
866 client_entry->hostname,
867 sizeof(client_entry->username));
868 if (!client_entry->nickname[0] && nickname) {
869 silc_parse_userfqdn(nickname, client_entry->nickname,
870 sizeof(client_entry->nickname),
871 client_entry->server,
872 sizeof(client_entry->server));
874 /* Normalize nickname */
875 nick = silc_identifier_check(client_entry->nickname,
876 strlen(client_entry->nickname),
877 SILC_STRING_UTF8, 128, NULL);
879 silc_rwlock_unlock(client_entry->internal.lock);
883 /* Format nickname */
884 silc_client_nickname_format(client, conn, client_entry,
885 client_entry == conn->local_entry);
887 /* Update cache entry */
888 silc_mutex_lock(conn->internal->lock);
889 silc_idcache_update_by_context(conn->internal->client_cache,
890 client_entry, NULL, nick, TRUE);
891 silc_mutex_unlock(conn->internal->lock);
892 client_entry->nickname_normalized = nick;
893 client_entry->internal.valid = TRUE;
895 client_entry->mode = mode;
897 silc_rwlock_unlock(client_entry->internal.lock);
900 /* Change a client's nickname. Must be called with `client_entry' locked. */
902 SilcBool silc_client_change_nickname(SilcClient client,
903 SilcClientConnection conn,
904 SilcClientEntry client_entry,
905 const char *new_nick,
906 SilcClientID *new_id,
907 const unsigned char *idp,
912 SILC_LOG_DEBUG(("Change nickname %s to %s", client_entry->nickname,
915 /* Normalize nickname */
916 tmp = silc_identifier_check(new_nick, strlen(new_nick),
917 SILC_STRING_UTF8, 128, NULL);
921 /* Update the client entry */
922 silc_mutex_lock(conn->internal->lock);
923 if (!silc_idcache_update_by_context(conn->internal->client_cache,
924 client_entry, new_id, tmp, TRUE)) {
926 silc_mutex_unlock(conn->internal->lock);
929 silc_mutex_unlock(conn->internal->lock);
931 memset(client_entry->nickname, 0, sizeof(client_entry->nickname));
932 memcpy(client_entry->nickname, new_nick, strlen(new_nick));
933 client_entry->nickname_normalized = tmp;
934 silc_client_nickname_format(client, conn, client_entry,
935 client_entry == conn->local_entry);
937 /* For my client entry, update ID and set new ID to packet stream */
938 if (client_entry == conn->local_entry) {
939 if (idp && idp_len) {
940 silc_buffer_enlarge(conn->internal->local_idp, idp_len);
941 silc_buffer_put(conn->internal->local_idp, idp, idp_len);
944 silc_packet_set_ids(conn->stream, SILC_ID_CLIENT, conn->local_id,
948 client_entry->internal.valid = TRUE;
952 /* Deletes the client entry and frees all memory. */
954 void silc_client_del_client_entry(SilcClient client,
955 SilcClientConnection conn,
956 SilcClientEntry client_entry)
958 silc_free(client_entry->realname);
959 silc_free(client_entry->nickname_normalized);
960 silc_free(client_entry->internal.key);
961 if (client_entry->public_key)
962 silc_pkcs_public_key_free(client_entry->public_key);
963 silc_hash_table_free(client_entry->channels);
964 if (client_entry->internal.send_key)
965 silc_cipher_free(client_entry->internal.send_key);
966 if (client_entry->internal.receive_key)
967 silc_cipher_free(client_entry->internal.receive_key);
968 if (client_entry->internal.hmac_send)
969 silc_hmac_free(client_entry->internal.hmac_send);
970 if (client_entry->internal.hmac_receive)
971 silc_hmac_free(client_entry->internal.hmac_receive);
972 silc_client_ftp_session_free_client(client, client_entry);
973 if (client_entry->internal.ke)
974 silc_client_abort_key_agreement(client, conn, client_entry);
975 silc_atomic_uninit8(&client_entry->internal.refcnt);
976 silc_rwlock_free(client_entry->internal.lock);
977 silc_free(client_entry);
980 /* Removes client from the cache by the client entry. */
982 SilcBool silc_client_del_client(SilcClient client, SilcClientConnection conn,
983 SilcClientEntry client_entry)
990 if (silc_atomic_sub_int8(&client_entry->internal.refcnt, 1) > 0)
993 SILC_LOG_DEBUG(("Deleting client %p", client_entry));
995 silc_mutex_lock(conn->internal->lock);
996 ret = silc_idcache_del_by_context(conn->internal->client_cache,
998 silc_mutex_unlock(conn->internal->lock);
1001 /* Remove from channels */
1002 silc_client_remove_from_channels(client, conn, client_entry);
1004 /* Free the client entry data */
1005 silc_client_del_client_entry(client, conn, client_entry);
1011 /* Internal routine used to find client by ID and if not found this creates
1012 new client entry and returns it. */
1014 SilcClientEntry silc_client_get_client(SilcClient client,
1015 SilcClientConnection conn,
1016 SilcClientID *client_id)
1018 SilcClientEntry client_entry;
1020 client_entry = silc_client_get_client_by_id(client, conn, client_id);
1021 if (!client_entry) {
1022 client_entry = silc_client_add_client(client, conn, NULL, NULL, NULL,
1026 silc_client_ref_client(client, conn, client_entry);
1029 return client_entry;
1034 void silc_client_lock_client(SilcClientEntry client_entry)
1036 silc_rwlock_rdlock(client_entry->internal.lock);
1041 void silc_client_unlock_client(SilcClientEntry client_entry)
1043 silc_rwlock_unlock(client_entry->internal.lock);
1046 /* Take reference of client entry */
1048 SilcClientEntry silc_client_ref_client(SilcClient client,
1049 SilcClientConnection conn,
1050 SilcClientEntry client_entry)
1052 silc_atomic_add_int8(&client_entry->internal.refcnt, 1);
1053 SILC_LOG_DEBUG(("Client %p refcnt %d->%d", client_entry,
1054 silc_atomic_get_int8(&client_entry->internal.refcnt) - 1,
1055 silc_atomic_get_int8(&client_entry->internal.refcnt)));
1056 return client_entry;
1059 /* Release reference of client entry */
1061 void silc_client_unref_client(SilcClient client, SilcClientConnection conn,
1062 SilcClientEntry client_entry)
1065 SILC_LOG_DEBUG(("Client %p refcnt %d->%d", client_entry,
1066 silc_atomic_get_int8(&client_entry->internal.refcnt),
1067 silc_atomic_get_int8(&client_entry->internal.refcnt) - 1));
1068 silc_client_del_client(client, conn, client_entry);
1072 /* Free client entry list */
1074 void silc_client_list_free(SilcClient client, SilcClientConnection conn,
1075 SilcDList client_list)
1077 SilcClientEntry client_entry;
1080 silc_dlist_start(client_list);
1081 while ((client_entry = silc_dlist_get(client_list)))
1082 silc_client_unref_client(client, conn, client_entry);
1084 silc_dlist_uninit(client_list);
1088 /* Formats the nickname of the client specified by the `client_entry'.
1089 If the format is specified by the application this will format the
1090 nickname and replace the old nickname in the client entry. If the
1091 format string is not specified then this function has no effect.
1092 Returns the client entry that was formatted. */
1094 SilcClientEntry silc_client_nickname_format(SilcClient client,
1095 SilcClientConnection conn,
1096 SilcClientEntry client_entry,
1100 char newnick[128 + 1];
1101 int i, off = 0, len;
1103 SilcClientEntry entry, unformatted = NULL;
1104 SilcBool formatted = FALSE;
1106 if (!client->internal->params->nickname_format[0])
1107 return client_entry;
1108 if (!client_entry->nickname[0])
1111 SILC_LOG_DEBUG(("Format nickname"));
1113 /* Get all clients with same nickname. Do not perform the formatting
1114 if there aren't any clients with same nickname unless the application
1115 is forcing us to do so. */
1116 clients = silc_client_get_clients_local_ext(client, conn,
1117 client_entry->nickname,
1121 if (silc_dlist_count(clients) == 1 && !priority &&
1122 !client->internal->params->nickname_force_format) {
1123 silc_client_list_free(client, conn, clients);
1124 return client_entry;
1127 /* Is the requested client formatted already */
1128 if (!silc_utf8_strcasecmp(client_entry->nickname,
1129 client_entry->nickname_normalized))
1132 if (client->internal->params->nickname_force_format)
1135 /* Find unformatted client entry */
1136 while ((entry = silc_dlist_get(clients))) {
1137 if (!entry->internal.valid)
1139 if (entry == client_entry)
1141 if (silc_utf8_strcasecmp(entry->nickname, entry->nickname_normalized)) {
1142 unformatted = entry;
1147 /* If there are no other unformatted clients and the requested client is
1148 unformatted, just return it. */
1149 if (!unformatted && !formatted) {
1150 silc_client_list_free(client, conn, clients);
1151 return client_entry;
1154 /* If priority formatting then the requested client will get the
1155 unformatted nickname, and the unformatted client will get a new
1156 formatted nickname. */
1159 /* Simply change the client's nickname to unformatted */
1160 if (!silc_client_nickname_parse(client, conn, client_entry->nickname,
1164 silc_snprintf(client_entry->nickname, sizeof(client_entry->nickname),
1170 /* There was no other unformatted client */
1171 silc_client_list_free(client, conn, clients);
1172 return client_entry;
1175 /* Now format the previously unformatted client */
1176 client_entry = unformatted;
1180 /* If already formatted just return it */
1182 silc_client_list_free(client, conn, clients);
1183 return client_entry;
1186 memset(newnick, 0, sizeof(newnick));
1187 cp = client->internal->params->nickname_format;
1197 if (!client_entry->nickname[0])
1199 len = strlen(client_entry->nickname);
1200 memcpy(&newnick[off], client_entry->nickname, len);
1204 /* Stripped hostname */
1205 if (!client_entry->hostname[0])
1207 len = strcspn(client_entry->hostname, ".");
1208 i = strcspn(client_entry->hostname, "-");
1211 memcpy(&newnick[off], client_entry->hostname, len);
1216 if (!client_entry->hostname[0])
1218 len = strlen(client_entry->hostname);
1219 memcpy(&newnick[off], client_entry->hostname, len);
1223 /* Ascending number */
1228 if (silc_dlist_count(clients) == 1)
1231 silc_dlist_start(clients);
1232 while ((entry = silc_dlist_get(clients))) {
1233 if (!silc_utf8_strncasecmp(entry->nickname, newnick, off))
1235 if (strlen(entry->nickname) <= off)
1237 num = atoi(&entry->nickname[off]);
1242 memset(tmp, 0, sizeof(tmp));
1243 silc_snprintf(tmp, sizeof(tmp) - 1, "%d", ++max);
1245 memcpy(&newnick[off], tmp, len);
1250 /* Some other character in the string */
1251 memcpy(&newnick[off], cp, 1);
1260 memcpy(client_entry->nickname, newnick, strlen(newnick));
1261 silc_client_list_free(client, conn, clients);
1263 return client_entry;
1266 /* Parses nickname according to nickname format string */
1268 SilcBool silc_client_nickname_parse(SilcClient client,
1269 SilcClientConnection conn,
1273 char *cp, s = 0, e = 0, *nick;
1277 if (!client->internal->params->nickname_format[0]) {
1282 if (!nickname || !nickname[0])
1285 cp = client->internal->params->nickname_format;
1303 /* Get separator character */
1316 /* Parse the nickname */
1320 if (strchr(nickname, s))
1321 nick = strchr(nickname, s) + 1;
1323 if (strchr(nick, e))
1324 len = strchr(nick, e) - nick;
1328 *ret_nick = silc_memdup(nick, len);
1332 SILC_LOG_DEBUG(("Parsed nickname: %s", *ret_nick));
1337 /************************ Channel Searching Locally *************************/
1339 /* Finds entry for channel by the channel name. Returns the entry or NULL
1340 if the entry was not found. It is found only if the client is joined
1343 SilcChannelEntry silc_client_get_channel(SilcClient client,
1344 SilcClientConnection conn,
1347 SilcIDCacheEntry id_cache;
1348 SilcChannelEntry entry;
1350 if (!client || !conn || !channel)
1353 SILC_LOG_DEBUG(("Find channel %s", channel));
1355 /* Normalize name for search */
1356 channel = silc_channel_name_check(channel, strlen(channel), SILC_STRING_UTF8,
1361 silc_mutex_lock(conn->internal->lock);
1363 if (!silc_idcache_find_by_name_one(conn->internal->channel_cache, channel,
1365 silc_mutex_unlock(conn->internal->lock);
1370 SILC_LOG_DEBUG(("Found"));
1372 entry = id_cache->context;
1375 silc_client_ref_channel(client, conn, entry);
1376 silc_mutex_unlock(conn->internal->lock);
1383 /* Finds entry for channel by the channel ID. Returns the entry or NULL
1384 if the entry was not found. It is found only if the client is joined
1387 SilcChannelEntry silc_client_get_channel_by_id(SilcClient client,
1388 SilcClientConnection conn,
1389 SilcChannelID *channel_id)
1391 SilcIDCacheEntry id_cache;
1392 SilcChannelEntry entry;
1394 if (!client || !conn || !channel_id)
1397 SILC_LOG_DEBUG(("Find channel by id %s",
1398 silc_id_render(channel_id, SILC_ID_CHANNEL)));
1400 silc_mutex_lock(conn->internal->lock);
1402 if (!silc_idcache_find_by_id_one(conn->internal->channel_cache, channel_id,
1404 silc_mutex_unlock(conn->internal->lock);
1408 SILC_LOG_DEBUG(("Found"));
1410 entry = id_cache->context;
1413 silc_client_ref_channel(client, conn, entry);
1414 silc_mutex_unlock(conn->internal->lock);
1419 /********************** Channel Resolving from Server ***********************/
1421 /* Channel resolving context */
1424 SilcGetChannelCallback completion;
1426 } *SilcClientGetChannelInternal;
1428 /* Resolving command callback */
1430 static SilcBool silc_client_get_channel_cb(SilcClient client,
1431 SilcClientConnection conn,
1432 SilcCommand command,
1438 SilcClientGetChannelInternal i = context;
1439 SilcChannelEntry entry;
1441 if (error != SILC_STATUS_OK) {
1442 SILC_LOG_DEBUG(("Resolving failed: %s", silc_get_status_message(error)));
1444 i->completion(client, conn, error, NULL, i->context);
1448 /* Add the returned channel to list */
1449 if (i->completion) {
1450 entry = va_arg(ap, SilcChannelEntry);
1451 silc_client_ref_channel(client, conn, entry);
1452 silc_dlist_add(i->channels, entry);
1455 if (status == SILC_STATUS_OK || status == SILC_STATUS_LIST_END) {
1456 /* Deliver the channels to the caller */
1457 if (i->completion) {
1458 SILC_LOG_DEBUG(("Resolved %d channels", silc_dlist_count(i->channels)));
1459 silc_dlist_start(i->channels);
1460 i->completion(client, conn, SILC_STATUS_OK, i->channels, i->context);
1468 silc_client_list_free_channels(client, conn, i->channels);
1473 /* Resolves channel entry from the server by the channel name. */
1475 void silc_client_get_channel_resolve(SilcClient client,
1476 SilcClientConnection conn,
1478 SilcGetChannelCallback completion,
1481 SilcClientGetChannelInternal i;
1483 if (!client || !conn || !channel_name || !completion)
1486 SILC_LOG_DEBUG(("Resolve channel %s", channel_name));
1488 i = silc_calloc(1, sizeof(*i));
1491 i->completion = completion;
1492 i->context = context;
1493 i->channels = silc_dlist_init();
1499 /* Send the command */
1500 if (!silc_client_command_send(client, conn, SILC_COMMAND_IDENTIFY,
1501 silc_client_get_channel_cb, i, 1,
1502 3, channel_name, strlen(channel_name))) {
1504 completion(client, conn, SILC_STATUS_ERR_RESOURCE_LIMIT, NULL, context);
1508 /* Resolves channel information from the server by the channel ID. */
1511 silc_client_get_channel_by_id_resolve(SilcClient client,
1512 SilcClientConnection conn,
1513 SilcChannelID *channel_id,
1514 SilcGetChannelCallback completion,
1517 SilcClientGetChannelInternal i;
1519 SilcUInt16 cmd_ident;
1521 if (!client || !conn || !channel_id || !completion)
1524 SILC_LOG_DEBUG(("Resolve channel by id %s",
1525 silc_id_render(channel_id, SILC_ID_CHANNEL)));
1527 i = silc_calloc(1, sizeof(*i));
1530 i->completion = completion;
1531 i->context = context;
1532 i->channels = silc_dlist_init();
1538 /* Send the command */
1539 idp = silc_id_payload_encode(channel_id, SILC_ID_CHANNEL);
1540 cmd_ident = silc_client_command_send(client, conn, SILC_COMMAND_IDENTIFY,
1541 silc_client_get_channel_cb, i, 1,
1542 5, silc_buffer_datalen(idp));
1543 silc_buffer_free(idp);
1544 if (!cmd_ident && completion)
1545 completion(client, conn, SILC_STATUS_ERR_RESOURCE_LIMIT, NULL, context);
1550 /************************* Channel Entry Routines ***************************/
1552 /* Add new channel entry to the ID Cache */
1554 SilcChannelEntry silc_client_add_channel(SilcClient client,
1555 SilcClientConnection conn,
1556 const char *channel_name,
1558 SilcChannelID *channel_id)
1560 SilcChannelEntry channel;
1561 char *channel_namec;
1563 SILC_LOG_DEBUG(("Start"));
1565 channel = silc_calloc(1, sizeof(*channel));
1569 silc_rwlock_alloc(&channel->internal.lock);
1570 silc_atomic_init16(&channel->internal.refcnt, 0);
1571 channel->id = *channel_id;
1572 channel->mode = mode;
1574 channel->channel_name = strdup(channel_name);
1575 if (!channel->channel_name) {
1580 channel->user_list = silc_hash_table_alloc(1, silc_hash_ptr, NULL, NULL,
1581 NULL, NULL, NULL, TRUE);
1582 if (!channel->user_list) {
1583 silc_free(channel->channel_name);
1588 /* Normalize channel name */
1589 channel_namec = silc_channel_name_check(channel_name, strlen(channel_name),
1590 SILC_STRING_UTF8, 256, NULL);
1591 if (!channel_namec) {
1592 silc_free(channel->channel_name);
1593 silc_hash_table_free(channel->user_list);
1598 silc_mutex_lock(conn->internal->lock);
1600 /* Add channel to cache, the normalized channel name is saved to cache */
1601 if (!silc_idcache_add(conn->internal->channel_cache, channel_namec,
1602 &channel->id, channel)) {
1603 silc_free(channel_namec);
1604 silc_free(channel->channel_name);
1605 silc_hash_table_free(channel->user_list);
1607 silc_mutex_unlock(conn->internal->lock);
1611 silc_mutex_unlock(conn->internal->lock);
1612 silc_client_ref_channel(client, conn, channel);
1614 SILC_LOG_DEBUG(("Added %p", channel));
1619 /* Removes channel from the cache by the channel entry. */
1621 SilcBool silc_client_del_channel(SilcClient client, SilcClientConnection conn,
1622 SilcChannelEntry channel)
1624 SilcIDCacheEntry id_cache;
1625 SilcBool ret = TRUE;
1633 if (silc_atomic_sub_int16(&channel->internal.refcnt, 1) > 0)
1636 SILC_LOG_DEBUG(("Deleting channel %p", channel));
1638 silc_mutex_lock(conn->internal->lock);
1639 if (silc_idcache_find_by_context(conn->internal->channel_cache, channel,
1641 namec = id_cache->name;
1642 ret = silc_idcache_del_by_context(conn->internal->channel_cache,
1646 silc_mutex_unlock(conn->internal->lock);
1651 silc_client_empty_channel(client, conn, channel);
1652 silc_hash_table_free(channel->user_list);
1653 silc_free(channel->channel_name);
1654 silc_free(channel->topic);
1655 if (channel->founder_key)
1656 silc_pkcs_public_key_free(channel->founder_key);
1657 if (channel->internal.send_key)
1658 silc_cipher_free(channel->internal.send_key);
1659 if (channel->internal.receive_key)
1660 silc_cipher_free(channel->internal.receive_key);
1661 if (channel->internal.hmac)
1662 silc_hmac_free(channel->internal.hmac);
1663 if (channel->internal.old_channel_keys) {
1664 silc_dlist_start(channel->internal.old_channel_keys);
1665 while ((key = silc_dlist_get(channel->internal.old_channel_keys)))
1666 silc_cipher_free(key);
1667 silc_dlist_uninit(channel->internal.old_channel_keys);
1669 if (channel->internal.old_hmacs) {
1670 silc_dlist_start(channel->internal.old_hmacs);
1671 while ((hmac = silc_dlist_get(channel->internal.old_hmacs)))
1672 silc_hmac_free(hmac);
1673 silc_dlist_uninit(channel->internal.old_hmacs);
1675 if (channel->channel_pubkeys)
1676 silc_argument_list_free(channel->channel_pubkeys,
1677 SILC_ARGUMENT_PUBLIC_KEY);
1678 silc_client_del_channel_private_keys(client, conn, channel);
1679 silc_atomic_uninit16(&channel->internal.refcnt);
1680 silc_rwlock_free(channel->internal.lock);
1681 silc_schedule_task_del_by_context(conn->client->schedule, channel);
1687 /* Replaces the channel ID of the `channel' to `new_id'. Returns FALSE
1688 if the ID could not be changed. This handles entry locking internally. */
1690 SilcBool silc_client_replace_channel_id(SilcClient client,
1691 SilcClientConnection conn,
1692 SilcChannelEntry channel,
1693 SilcChannelID *new_id)
1695 SilcBool ret = FALSE;
1700 SILC_LOG_DEBUG(("Old Channel ID id(%s)",
1701 silc_id_render(&channel->id, SILC_ID_CHANNEL)));
1702 SILC_LOG_DEBUG(("New Channel ID id(%s)",
1703 silc_id_render(new_id, SILC_ID_CHANNEL)));
1706 silc_rwlock_wrlock(channel->internal.lock);
1707 silc_mutex_lock(conn->internal->lock);
1708 silc_idcache_update_by_context(conn->internal->channel_cache, channel,
1709 new_id, NULL, FALSE);
1710 silc_mutex_unlock(conn->internal->lock);
1711 silc_rwlock_unlock(channel->internal.lock);
1718 void silc_client_lock_channel(SilcChannelEntry channel_entry)
1720 silc_rwlock_rdlock(channel_entry->internal.lock);
1723 /* Unlock channel */
1725 void silc_client_unlock_channel(SilcChannelEntry channel_entry)
1727 silc_rwlock_unlock(channel_entry->internal.lock);
1730 /* Take reference of channel entry */
1732 SilcChannelEntry silc_client_ref_channel(SilcClient client,
1733 SilcClientConnection conn,
1734 SilcChannelEntry channel_entry)
1736 silc_atomic_add_int16(&channel_entry->internal.refcnt, 1);
1737 SILC_LOG_DEBUG(("Channel %p refcnt %d->%d", channel_entry,
1738 silc_atomic_get_int16(&channel_entry->internal.refcnt) - 1,
1739 silc_atomic_get_int16(&channel_entry->internal.refcnt)));
1740 return channel_entry;
1743 /* Release reference of channel entry */
1745 void silc_client_unref_channel(SilcClient client, SilcClientConnection conn,
1746 SilcChannelEntry channel_entry)
1748 if (channel_entry) {
1749 SILC_LOG_DEBUG(("Channel %p refcnt %d->%d", channel_entry,
1750 silc_atomic_get_int16(&channel_entry->internal.refcnt),
1751 silc_atomic_get_int16(&channel_entry->internal.refcnt)
1753 silc_client_del_channel(client, conn, channel_entry);
1757 /* Free channel entry list */
1759 void silc_client_list_free_channels(SilcClient client,
1760 SilcClientConnection conn,
1761 SilcDList channel_list)
1763 SilcChannelEntry channel_entry;
1766 silc_dlist_start(channel_list);
1767 while ((channel_entry = silc_dlist_get(channel_list)))
1768 silc_client_unref_channel(client, conn, channel_entry);
1770 silc_dlist_uninit(channel_list);
1774 /************************* Server Searching Locally *************************/
1776 /* Finds entry for server by the server name. */
1778 SilcServerEntry silc_client_get_server(SilcClient client,
1779 SilcClientConnection conn,
1782 SilcIDCacheEntry id_cache;
1783 SilcServerEntry entry;
1785 if (!client || !conn || !server_name)
1788 SILC_LOG_DEBUG(("Find server by name %s", server_name));
1790 /* Normalize server name for search */
1791 server_name = silc_identifier_check(server_name, strlen(server_name),
1792 SILC_STRING_UTF8, 256, NULL);
1796 silc_mutex_lock(conn->internal->lock);
1798 if (!silc_idcache_find_by_name_one(conn->internal->server_cache,
1799 server_name, &id_cache)) {
1800 silc_free(server_name);
1801 silc_mutex_unlock(conn->internal->lock);
1805 SILC_LOG_DEBUG(("Found"));
1808 entry = id_cache->context;
1809 silc_client_ref_server(client, conn, entry);
1811 silc_mutex_unlock(conn->internal->lock);
1813 silc_free(server_name);
1818 /* Finds entry for server by the server ID. */
1820 SilcServerEntry silc_client_get_server_by_id(SilcClient client,
1821 SilcClientConnection conn,
1822 SilcServerID *server_id)
1824 SilcIDCacheEntry id_cache;
1825 SilcServerEntry entry;
1827 if (!client || !conn || !server_id)
1830 SILC_LOG_DEBUG(("Find server by id %s",
1831 silc_id_render(server_id, SILC_ID_SERVER)));
1833 silc_mutex_lock(conn->internal->lock);
1835 if (!silc_idcache_find_by_id_one(conn->internal->server_cache,
1836 server_id, &id_cache)) {
1837 silc_mutex_unlock(conn->internal->lock);
1841 SILC_LOG_DEBUG(("Found"));
1844 entry = id_cache->context;
1845 silc_client_ref_server(client, conn, entry);
1847 silc_mutex_unlock(conn->internal->lock);
1852 /*********************** Server Resolving from Server ***********************/
1854 /* Resolving context */
1857 SilcGetServerCallback completion;
1859 } *SilcClientGetServerInternal;
1861 /* Resolving command callback */
1863 static SilcBool silc_client_get_server_cb(SilcClient client,
1864 SilcClientConnection conn,
1865 SilcCommand command,
1871 SilcClientGetServerInternal i = context;
1872 SilcServerEntry server;
1874 if (error != SILC_STATUS_OK) {
1875 SILC_LOG_DEBUG(("Resolving failed: %s", silc_get_status_message(error)));
1877 i->completion(client, conn, error, NULL, i->context);
1881 /* Add the returned servers to list */
1882 if (i->completion) {
1883 server = va_arg(ap, SilcServerEntry);
1884 silc_client_ref_server(client, conn, server);
1885 silc_dlist_add(i->servers, server);
1886 server->internal.resolve_cmd_ident = 0;
1889 if (status == SILC_STATUS_OK || status == SILC_STATUS_LIST_END) {
1890 /* Deliver the servers to the caller */
1891 if (i->completion) {
1892 SILC_LOG_DEBUG(("Resolved %d servers", silc_dlist_count(i->servers)));
1893 silc_dlist_start(i->servers);
1894 i->completion(client, conn, SILC_STATUS_OK, i->servers, i->context);
1902 silc_client_list_free_servers(client, conn, i->servers);
1907 /* Resolve server by server ID */
1910 silc_client_get_server_by_id_resolve(SilcClient client,
1911 SilcClientConnection conn,
1912 SilcServerID *server_id,
1913 SilcGetServerCallback completion,
1916 SilcClientGetServerInternal i;
1917 SilcServerEntry server;
1919 SilcUInt16 cmd_ident;
1921 if (!client || !conn || !server_id || !completion)
1924 SILC_LOG_DEBUG(("Resolve server by id %s",
1925 silc_id_render(server_id, SILC_ID_SERVER)));
1927 i = silc_calloc(1, sizeof(*i));
1930 i->completion = completion;
1931 i->context = context;
1932 i->servers = silc_dlist_init();
1938 /* Attach to resolving, if on going */
1939 server = silc_client_get_server_by_id(client, conn, server_id);
1940 if (server && server->internal.resolve_cmd_ident) {
1941 SILC_LOG_DEBUG(("Attach to existing resolving"));
1942 silc_client_unref_server(client, conn, server);
1943 silc_client_command_pending(conn, SILC_COMMAND_NONE,
1944 server->internal.resolve_cmd_ident,
1945 silc_client_get_server_cb, i);
1946 return server->internal.resolve_cmd_ident;
1949 /* Send the command */
1950 idp = silc_id_payload_encode(server_id, SILC_ID_SERVER);
1951 cmd_ident = silc_client_command_send(client, conn, SILC_COMMAND_IDENTIFY,
1952 silc_client_get_server_cb, i, 1,
1953 5, silc_buffer_datalen(idp));
1954 silc_buffer_free(idp);
1955 if (!cmd_ident && completion)
1956 completion(client, conn, SILC_STATUS_ERR_RESOURCE_LIMIT, NULL, context);
1958 if (server && cmd_ident)
1959 server->internal.resolve_cmd_ident = cmd_ident;
1961 silc_client_unref_server(client, conn, server);
1966 /************************** Server Entry Routines ***************************/
1968 /* Add new server entry */
1970 SilcServerEntry silc_client_add_server(SilcClient client,
1971 SilcClientConnection conn,
1972 const char *server_name,
1973 const char *server_info,
1974 SilcServerID *server_id)
1976 SilcServerEntry server_entry;
1977 char *server_namec = NULL;
1982 SILC_LOG_DEBUG(("Adding new server %s", server_name));
1984 server_entry = silc_calloc(1, sizeof(*server_entry));
1988 silc_rwlock_alloc(&server_entry->internal.lock);
1989 silc_atomic_init8(&server_entry->internal.refcnt, 0);
1990 server_entry->id = *server_id;
1992 server_entry->server_name = strdup(server_name);
1994 server_entry->server_info = strdup(server_info);
1996 /* Normalize server name */
1998 server_namec = silc_identifier_check(server_name, strlen(server_name),
1999 SILC_STRING_UTF8, 256, NULL);
2000 if (!server_namec) {
2001 silc_free(server_entry->server_name);
2002 silc_free(server_entry->server_info);
2003 silc_free(server_entry);
2008 silc_mutex_lock(conn->internal->lock);
2010 /* Add server to cache */
2011 if (!silc_idcache_add(conn->internal->server_cache, server_namec,
2012 &server_entry->id, server_entry)) {
2013 silc_free(server_namec);
2014 silc_free(server_entry->server_name);
2015 silc_free(server_entry->server_info);
2016 silc_free(server_entry);
2017 silc_mutex_unlock(conn->internal->lock);
2021 silc_mutex_unlock(conn->internal->lock);
2022 silc_client_ref_server(client, conn, server_entry);
2024 SILC_LOG_DEBUG(("Added %p", server_entry));
2026 return server_entry;
2029 /* Removes server from the cache by the server entry. */
2031 SilcBool silc_client_del_server(SilcClient client, SilcClientConnection conn,
2032 SilcServerEntry server)
2034 SilcIDCacheEntry id_cache;
2035 SilcBool ret = TRUE;
2041 if (silc_atomic_sub_int8(&server->internal.refcnt, 1) > 0)
2044 SILC_LOG_DEBUG(("Deleting server %p", server));
2046 silc_mutex_lock(conn->internal->lock);
2047 if (silc_idcache_find_by_context(conn->internal->server_cache, server,
2049 namec = id_cache->name;
2050 ret = silc_idcache_del_by_context(conn->internal->server_cache,
2054 silc_mutex_unlock(conn->internal->lock);
2056 silc_free(server->server_name);
2057 silc_free(server->server_info);
2058 if (server->public_key)
2059 silc_pkcs_public_key_free(server->public_key);
2060 silc_atomic_uninit8(&server->internal.refcnt);
2061 silc_rwlock_free(server->internal.lock);
2067 /* Updates the `server_entry' with the new information sent as argument. */
2069 void silc_client_update_server(SilcClient client,
2070 SilcClientConnection conn,
2071 SilcServerEntry server_entry,
2072 const char *server_name,
2073 const char *server_info)
2075 char *server_namec = NULL;
2077 SILC_LOG_DEBUG(("Updating server %p", server_entry));
2080 (!server_entry->server_name ||
2081 !silc_utf8_strcasecmp(server_entry->server_name, server_name))) {
2083 server_namec = silc_identifier_check(server_name, strlen(server_name),
2084 SILC_STRING_UTF8, 256, NULL);
2088 silc_free(server_entry->server_name);
2089 server_entry->server_name = strdup(server_name);
2090 if (!server_entry->server_name)
2093 /* Update cache entry */
2094 silc_mutex_lock(conn->internal->lock);
2095 silc_idcache_update_by_context(conn->internal->server_cache, server_entry,
2096 NULL, server_namec, TRUE);
2097 silc_mutex_unlock(conn->internal->lock);
2101 (!server_entry->server_info ||
2102 !silc_utf8_strcasecmp(server_entry->server_info, server_info))) {
2103 silc_free(server_entry->server_info);
2104 server_entry->server_info = strdup(server_info);
2110 void silc_client_lock_server(SilcServerEntry server_entry)
2112 silc_rwlock_rdlock(server_entry->internal.lock);
2117 void silc_client_unlock_server(SilcServerEntry server_entry)
2119 silc_rwlock_unlock(server_entry->internal.lock);
2122 /* Take reference of server entry */
2124 SilcServerEntry silc_client_ref_server(SilcClient client,
2125 SilcClientConnection conn,
2126 SilcServerEntry server_entry)
2128 silc_atomic_add_int8(&server_entry->internal.refcnt, 1);
2129 SILC_LOG_DEBUG(("Server %p refcnt %d->%d", server_entry,
2130 silc_atomic_get_int8(&server_entry->internal.refcnt) - 1,
2131 silc_atomic_get_int8(&server_entry->internal.refcnt)));
2132 return server_entry;
2135 /* Release reference of server entry */
2137 void silc_client_unref_server(SilcClient client, SilcClientConnection conn,
2138 SilcServerEntry server_entry)
2141 SILC_LOG_DEBUG(("Server %p refcnt %d->%d", server_entry,
2142 silc_atomic_get_int8(&server_entry->internal.refcnt),
2143 silc_atomic_get_int8(&server_entry->internal.refcnt)
2145 silc_client_del_server(client, conn, server_entry);
2149 /* Free server entry list */
2151 void silc_client_list_free_servers(SilcClient client,
2152 SilcClientConnection conn,
2153 SilcDList server_list)
2155 SilcServerEntry server_entry;
2158 silc_dlist_start(server_list);
2159 while ((server_entry = silc_dlist_get(server_list)))
2160 silc_client_unref_server(client, conn, server_entry);
2162 silc_dlist_uninit(server_list);