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 (client_entry->nickname_normalized &&
1129 !silc_utf8_strcasecmp(client_entry->nickname,
1130 client_entry->nickname_normalized))
1133 if (client->internal->params->nickname_force_format)
1136 /* Find unformatted client entry */
1137 while ((entry = silc_dlist_get(clients))) {
1138 if (!entry->internal.valid)
1140 if (entry == client_entry)
1142 if (silc_utf8_strcasecmp(entry->nickname, entry->nickname_normalized)) {
1143 unformatted = entry;
1148 /* If there are no other unformatted clients and the requested client is
1149 unformatted, just return it. */
1150 if (!unformatted && !formatted) {
1151 silc_client_list_free(client, conn, clients);
1152 return client_entry;
1155 /* If priority formatting then the requested client will get the
1156 unformatted nickname, and the unformatted client will get a new
1157 formatted nickname. */
1160 /* Simply change the client's nickname to unformatted */
1161 if (!silc_client_nickname_parse(client, conn, client_entry->nickname,
1165 silc_snprintf(client_entry->nickname, sizeof(client_entry->nickname),
1171 /* There was no other unformatted client */
1172 silc_client_list_free(client, conn, clients);
1173 return client_entry;
1176 /* Now format the previously unformatted client */
1177 client_entry = unformatted;
1181 /* If already formatted just return it */
1183 silc_client_list_free(client, conn, clients);
1184 return client_entry;
1187 memset(newnick, 0, sizeof(newnick));
1188 cp = client->internal->params->nickname_format;
1198 if (!client_entry->nickname[0])
1200 len = strlen(client_entry->nickname);
1201 memcpy(&newnick[off], client_entry->nickname, len);
1205 /* Stripped hostname */
1206 if (!client_entry->hostname[0])
1208 len = strcspn(client_entry->hostname, ".");
1209 i = strcspn(client_entry->hostname, "-");
1212 memcpy(&newnick[off], client_entry->hostname, len);
1217 if (!client_entry->hostname[0])
1219 len = strlen(client_entry->hostname);
1220 memcpy(&newnick[off], client_entry->hostname, len);
1224 /* Ascending number */
1229 if (silc_dlist_count(clients) == 1)
1232 silc_dlist_start(clients);
1233 while ((entry = silc_dlist_get(clients))) {
1234 if (!silc_utf8_strncasecmp(entry->nickname, newnick, off))
1236 if (strlen(entry->nickname) <= off)
1238 num = atoi(&entry->nickname[off]);
1243 memset(tmp, 0, sizeof(tmp));
1244 silc_snprintf(tmp, sizeof(tmp) - 1, "%d", ++max);
1246 memcpy(&newnick[off], tmp, len);
1251 /* Some other character in the string */
1252 memcpy(&newnick[off], cp, 1);
1261 memcpy(client_entry->nickname, newnick, strlen(newnick));
1262 silc_client_list_free(client, conn, clients);
1264 return client_entry;
1267 /* Parses nickname according to nickname format string */
1269 SilcBool silc_client_nickname_parse(SilcClient client,
1270 SilcClientConnection conn,
1274 char *cp, s = 0, e = 0, *nick;
1278 if (!client->internal->params->nickname_format[0]) {
1283 if (!nickname || !nickname[0])
1286 cp = client->internal->params->nickname_format;
1304 /* Get separator character */
1317 /* Parse the nickname */
1321 if (strchr(nickname, s))
1322 nick = strchr(nickname, s) + 1;
1324 if (strchr(nick, e))
1325 len = strchr(nick, e) - nick;
1329 *ret_nick = silc_memdup(nick, len);
1333 SILC_LOG_DEBUG(("Parsed nickname: %s", *ret_nick));
1338 /************************ Channel Searching Locally *************************/
1340 /* Finds entry for channel by the channel name. Returns the entry or NULL
1341 if the entry was not found. It is found only if the client is joined
1344 SilcChannelEntry silc_client_get_channel(SilcClient client,
1345 SilcClientConnection conn,
1348 SilcIDCacheEntry id_cache;
1349 SilcChannelEntry entry;
1351 if (!client || !conn || !channel)
1354 SILC_LOG_DEBUG(("Find channel %s", channel));
1356 /* Normalize name for search */
1357 channel = silc_channel_name_check(channel, strlen(channel), SILC_STRING_UTF8,
1362 silc_mutex_lock(conn->internal->lock);
1364 if (!silc_idcache_find_by_name_one(conn->internal->channel_cache, channel,
1366 silc_mutex_unlock(conn->internal->lock);
1371 SILC_LOG_DEBUG(("Found"));
1373 entry = id_cache->context;
1376 silc_client_ref_channel(client, conn, entry);
1377 silc_mutex_unlock(conn->internal->lock);
1384 /* Finds entry for channel by the channel ID. Returns the entry or NULL
1385 if the entry was not found. It is found only if the client is joined
1388 SilcChannelEntry silc_client_get_channel_by_id(SilcClient client,
1389 SilcClientConnection conn,
1390 SilcChannelID *channel_id)
1392 SilcIDCacheEntry id_cache;
1393 SilcChannelEntry entry;
1395 if (!client || !conn || !channel_id)
1398 SILC_LOG_DEBUG(("Find channel by id %s",
1399 silc_id_render(channel_id, SILC_ID_CHANNEL)));
1401 silc_mutex_lock(conn->internal->lock);
1403 if (!silc_idcache_find_by_id_one(conn->internal->channel_cache, channel_id,
1405 silc_mutex_unlock(conn->internal->lock);
1409 SILC_LOG_DEBUG(("Found"));
1411 entry = id_cache->context;
1414 silc_client_ref_channel(client, conn, entry);
1415 silc_mutex_unlock(conn->internal->lock);
1420 /********************** Channel Resolving from Server ***********************/
1422 /* Channel resolving context */
1425 SilcGetChannelCallback completion;
1427 } *SilcClientGetChannelInternal;
1429 /* Resolving command callback */
1431 static SilcBool silc_client_get_channel_cb(SilcClient client,
1432 SilcClientConnection conn,
1433 SilcCommand command,
1439 SilcClientGetChannelInternal i = context;
1440 SilcChannelEntry entry;
1442 if (error != SILC_STATUS_OK) {
1443 SILC_LOG_DEBUG(("Resolving failed: %s", silc_get_status_message(error)));
1445 i->completion(client, conn, error, NULL, i->context);
1449 /* Add the returned channel to list */
1450 if (i->completion) {
1451 entry = va_arg(ap, SilcChannelEntry);
1452 silc_client_ref_channel(client, conn, entry);
1453 silc_dlist_add(i->channels, entry);
1456 if (status == SILC_STATUS_OK || status == SILC_STATUS_LIST_END) {
1457 /* Deliver the channels to the caller */
1458 if (i->completion) {
1459 SILC_LOG_DEBUG(("Resolved %d channels", silc_dlist_count(i->channels)));
1460 silc_dlist_start(i->channels);
1461 i->completion(client, conn, SILC_STATUS_OK, i->channels, i->context);
1469 silc_client_list_free_channels(client, conn, i->channels);
1474 /* Resolves channel entry from the server by the channel name. */
1476 void silc_client_get_channel_resolve(SilcClient client,
1477 SilcClientConnection conn,
1479 SilcGetChannelCallback completion,
1482 SilcClientGetChannelInternal i;
1484 if (!client || !conn || !channel_name || !completion)
1487 SILC_LOG_DEBUG(("Resolve channel %s", channel_name));
1489 i = silc_calloc(1, sizeof(*i));
1492 i->completion = completion;
1493 i->context = context;
1494 i->channels = silc_dlist_init();
1500 /* Send the command */
1501 if (!silc_client_command_send(client, conn, SILC_COMMAND_IDENTIFY,
1502 silc_client_get_channel_cb, i, 1,
1503 3, channel_name, strlen(channel_name))) {
1505 completion(client, conn, SILC_STATUS_ERR_RESOURCE_LIMIT, NULL, context);
1509 /* Resolves channel information from the server by the channel ID. */
1512 silc_client_get_channel_by_id_resolve(SilcClient client,
1513 SilcClientConnection conn,
1514 SilcChannelID *channel_id,
1515 SilcGetChannelCallback completion,
1518 SilcClientGetChannelInternal i;
1520 SilcUInt16 cmd_ident;
1522 if (!client || !conn || !channel_id || !completion)
1525 SILC_LOG_DEBUG(("Resolve channel by id %s",
1526 silc_id_render(channel_id, SILC_ID_CHANNEL)));
1528 i = silc_calloc(1, sizeof(*i));
1531 i->completion = completion;
1532 i->context = context;
1533 i->channels = silc_dlist_init();
1539 /* Send the command */
1540 idp = silc_id_payload_encode(channel_id, SILC_ID_CHANNEL);
1541 cmd_ident = silc_client_command_send(client, conn, SILC_COMMAND_IDENTIFY,
1542 silc_client_get_channel_cb, i, 1,
1543 5, silc_buffer_datalen(idp));
1544 silc_buffer_free(idp);
1545 if (!cmd_ident && completion)
1546 completion(client, conn, SILC_STATUS_ERR_RESOURCE_LIMIT, NULL, context);
1551 /************************* Channel Entry Routines ***************************/
1553 /* Add new channel entry to the ID Cache */
1555 SilcChannelEntry silc_client_add_channel(SilcClient client,
1556 SilcClientConnection conn,
1557 const char *channel_name,
1559 SilcChannelID *channel_id)
1561 SilcChannelEntry channel;
1562 char *channel_namec;
1564 SILC_LOG_DEBUG(("Start"));
1566 channel = silc_calloc(1, sizeof(*channel));
1570 silc_rwlock_alloc(&channel->internal.lock);
1571 silc_atomic_init16(&channel->internal.refcnt, 0);
1572 channel->id = *channel_id;
1573 channel->mode = mode;
1575 channel->channel_name = strdup(channel_name);
1576 if (!channel->channel_name) {
1581 channel->user_list = silc_hash_table_alloc(1, silc_hash_ptr, NULL, NULL,
1582 NULL, NULL, NULL, TRUE);
1583 if (!channel->user_list) {
1584 silc_free(channel->channel_name);
1589 /* Normalize channel name */
1590 channel_namec = silc_channel_name_check(channel_name, strlen(channel_name),
1591 SILC_STRING_UTF8, 256, NULL);
1592 if (!channel_namec) {
1593 silc_free(channel->channel_name);
1594 silc_hash_table_free(channel->user_list);
1599 silc_mutex_lock(conn->internal->lock);
1601 /* Add channel to cache, the normalized channel name is saved to cache */
1602 if (!silc_idcache_add(conn->internal->channel_cache, channel_namec,
1603 &channel->id, channel)) {
1604 silc_free(channel_namec);
1605 silc_free(channel->channel_name);
1606 silc_hash_table_free(channel->user_list);
1608 silc_mutex_unlock(conn->internal->lock);
1612 silc_mutex_unlock(conn->internal->lock);
1613 silc_client_ref_channel(client, conn, channel);
1615 SILC_LOG_DEBUG(("Added %p", channel));
1620 /* Removes channel from the cache by the channel entry. */
1622 SilcBool silc_client_del_channel(SilcClient client, SilcClientConnection conn,
1623 SilcChannelEntry channel)
1625 SilcIDCacheEntry id_cache;
1626 SilcBool ret = TRUE;
1634 if (silc_atomic_sub_int16(&channel->internal.refcnt, 1) > 0)
1637 SILC_LOG_DEBUG(("Deleting channel %p", channel));
1639 silc_mutex_lock(conn->internal->lock);
1640 if (silc_idcache_find_by_context(conn->internal->channel_cache, channel,
1642 namec = id_cache->name;
1643 ret = silc_idcache_del_by_context(conn->internal->channel_cache,
1647 silc_mutex_unlock(conn->internal->lock);
1652 silc_client_empty_channel(client, conn, channel);
1653 silc_hash_table_free(channel->user_list);
1654 silc_free(channel->channel_name);
1655 silc_free(channel->topic);
1656 if (channel->founder_key)
1657 silc_pkcs_public_key_free(channel->founder_key);
1658 if (channel->internal.send_key)
1659 silc_cipher_free(channel->internal.send_key);
1660 if (channel->internal.receive_key)
1661 silc_cipher_free(channel->internal.receive_key);
1662 if (channel->internal.hmac)
1663 silc_hmac_free(channel->internal.hmac);
1664 if (channel->internal.old_channel_keys) {
1665 silc_dlist_start(channel->internal.old_channel_keys);
1666 while ((key = silc_dlist_get(channel->internal.old_channel_keys)))
1667 silc_cipher_free(key);
1668 silc_dlist_uninit(channel->internal.old_channel_keys);
1670 if (channel->internal.old_hmacs) {
1671 silc_dlist_start(channel->internal.old_hmacs);
1672 while ((hmac = silc_dlist_get(channel->internal.old_hmacs)))
1673 silc_hmac_free(hmac);
1674 silc_dlist_uninit(channel->internal.old_hmacs);
1676 if (channel->channel_pubkeys)
1677 silc_argument_list_free(channel->channel_pubkeys,
1678 SILC_ARGUMENT_PUBLIC_KEY);
1679 silc_client_del_channel_private_keys(client, conn, channel);
1680 silc_atomic_uninit16(&channel->internal.refcnt);
1681 silc_rwlock_free(channel->internal.lock);
1682 silc_schedule_task_del_by_context(conn->client->schedule, channel);
1688 /* Replaces the channel ID of the `channel' to `new_id'. Returns FALSE
1689 if the ID could not be changed. This handles entry locking internally. */
1691 SilcBool silc_client_replace_channel_id(SilcClient client,
1692 SilcClientConnection conn,
1693 SilcChannelEntry channel,
1694 SilcChannelID *new_id)
1696 SilcBool ret = FALSE;
1701 SILC_LOG_DEBUG(("Old Channel ID id(%s)",
1702 silc_id_render(&channel->id, SILC_ID_CHANNEL)));
1703 SILC_LOG_DEBUG(("New Channel ID id(%s)",
1704 silc_id_render(new_id, SILC_ID_CHANNEL)));
1707 silc_rwlock_wrlock(channel->internal.lock);
1708 silc_mutex_lock(conn->internal->lock);
1709 silc_idcache_update_by_context(conn->internal->channel_cache, channel,
1710 new_id, NULL, FALSE);
1711 silc_mutex_unlock(conn->internal->lock);
1712 silc_rwlock_unlock(channel->internal.lock);
1719 void silc_client_lock_channel(SilcChannelEntry channel_entry)
1721 silc_rwlock_rdlock(channel_entry->internal.lock);
1724 /* Unlock channel */
1726 void silc_client_unlock_channel(SilcChannelEntry channel_entry)
1728 silc_rwlock_unlock(channel_entry->internal.lock);
1731 /* Take reference of channel entry */
1733 SilcChannelEntry silc_client_ref_channel(SilcClient client,
1734 SilcClientConnection conn,
1735 SilcChannelEntry channel_entry)
1737 silc_atomic_add_int16(&channel_entry->internal.refcnt, 1);
1738 SILC_LOG_DEBUG(("Channel %p refcnt %d->%d", channel_entry,
1739 silc_atomic_get_int16(&channel_entry->internal.refcnt) - 1,
1740 silc_atomic_get_int16(&channel_entry->internal.refcnt)));
1741 return channel_entry;
1744 /* Release reference of channel entry */
1746 void silc_client_unref_channel(SilcClient client, SilcClientConnection conn,
1747 SilcChannelEntry channel_entry)
1749 if (channel_entry) {
1750 SILC_LOG_DEBUG(("Channel %p refcnt %d->%d", channel_entry,
1751 silc_atomic_get_int16(&channel_entry->internal.refcnt),
1752 silc_atomic_get_int16(&channel_entry->internal.refcnt)
1754 silc_client_del_channel(client, conn, channel_entry);
1758 /* Free channel entry list */
1760 void silc_client_list_free_channels(SilcClient client,
1761 SilcClientConnection conn,
1762 SilcDList channel_list)
1764 SilcChannelEntry channel_entry;
1767 silc_dlist_start(channel_list);
1768 while ((channel_entry = silc_dlist_get(channel_list)))
1769 silc_client_unref_channel(client, conn, channel_entry);
1771 silc_dlist_uninit(channel_list);
1775 /************************* Server Searching Locally *************************/
1777 /* Finds entry for server by the server name. */
1779 SilcServerEntry silc_client_get_server(SilcClient client,
1780 SilcClientConnection conn,
1783 SilcIDCacheEntry id_cache;
1784 SilcServerEntry entry;
1786 if (!client || !conn || !server_name)
1789 SILC_LOG_DEBUG(("Find server by name %s", server_name));
1791 /* Normalize server name for search */
1792 server_name = silc_identifier_check(server_name, strlen(server_name),
1793 SILC_STRING_UTF8, 256, NULL);
1797 silc_mutex_lock(conn->internal->lock);
1799 if (!silc_idcache_find_by_name_one(conn->internal->server_cache,
1800 server_name, &id_cache)) {
1801 silc_free(server_name);
1802 silc_mutex_unlock(conn->internal->lock);
1806 SILC_LOG_DEBUG(("Found"));
1809 entry = id_cache->context;
1810 silc_client_ref_server(client, conn, entry);
1812 silc_mutex_unlock(conn->internal->lock);
1814 silc_free(server_name);
1819 /* Finds entry for server by the server ID. */
1821 SilcServerEntry silc_client_get_server_by_id(SilcClient client,
1822 SilcClientConnection conn,
1823 SilcServerID *server_id)
1825 SilcIDCacheEntry id_cache;
1826 SilcServerEntry entry;
1828 if (!client || !conn || !server_id)
1831 SILC_LOG_DEBUG(("Find server by id %s",
1832 silc_id_render(server_id, SILC_ID_SERVER)));
1834 silc_mutex_lock(conn->internal->lock);
1836 if (!silc_idcache_find_by_id_one(conn->internal->server_cache,
1837 server_id, &id_cache)) {
1838 silc_mutex_unlock(conn->internal->lock);
1842 SILC_LOG_DEBUG(("Found"));
1845 entry = id_cache->context;
1846 silc_client_ref_server(client, conn, entry);
1848 silc_mutex_unlock(conn->internal->lock);
1853 /*********************** Server Resolving from Server ***********************/
1855 /* Resolving context */
1858 SilcGetServerCallback completion;
1860 } *SilcClientGetServerInternal;
1862 /* Resolving command callback */
1864 static SilcBool silc_client_get_server_cb(SilcClient client,
1865 SilcClientConnection conn,
1866 SilcCommand command,
1872 SilcClientGetServerInternal i = context;
1873 SilcServerEntry server;
1875 if (error != SILC_STATUS_OK) {
1876 SILC_LOG_DEBUG(("Resolving failed: %s", silc_get_status_message(error)));
1878 i->completion(client, conn, error, NULL, i->context);
1882 /* Add the returned servers to list */
1883 if (i->completion) {
1884 server = va_arg(ap, SilcServerEntry);
1885 silc_client_ref_server(client, conn, server);
1886 silc_dlist_add(i->servers, server);
1887 server->internal.resolve_cmd_ident = 0;
1890 if (status == SILC_STATUS_OK || status == SILC_STATUS_LIST_END) {
1891 /* Deliver the servers to the caller */
1892 if (i->completion) {
1893 SILC_LOG_DEBUG(("Resolved %d servers", silc_dlist_count(i->servers)));
1894 silc_dlist_start(i->servers);
1895 i->completion(client, conn, SILC_STATUS_OK, i->servers, i->context);
1903 silc_client_list_free_servers(client, conn, i->servers);
1908 /* Resolve server by server ID */
1911 silc_client_get_server_by_id_resolve(SilcClient client,
1912 SilcClientConnection conn,
1913 SilcServerID *server_id,
1914 SilcGetServerCallback completion,
1917 SilcClientGetServerInternal i;
1918 SilcServerEntry server;
1920 SilcUInt16 cmd_ident;
1922 if (!client || !conn || !server_id || !completion)
1925 SILC_LOG_DEBUG(("Resolve server by id %s",
1926 silc_id_render(server_id, SILC_ID_SERVER)));
1928 i = silc_calloc(1, sizeof(*i));
1931 i->completion = completion;
1932 i->context = context;
1933 i->servers = silc_dlist_init();
1939 /* Attach to resolving, if on going */
1940 server = silc_client_get_server_by_id(client, conn, server_id);
1941 if (server && server->internal.resolve_cmd_ident) {
1942 SILC_LOG_DEBUG(("Attach to existing resolving"));
1943 silc_client_unref_server(client, conn, server);
1944 silc_client_command_pending(conn, SILC_COMMAND_NONE,
1945 server->internal.resolve_cmd_ident,
1946 silc_client_get_server_cb, i);
1947 return server->internal.resolve_cmd_ident;
1950 /* Send the command */
1951 idp = silc_id_payload_encode(server_id, SILC_ID_SERVER);
1952 cmd_ident = silc_client_command_send(client, conn, SILC_COMMAND_IDENTIFY,
1953 silc_client_get_server_cb, i, 1,
1954 5, silc_buffer_datalen(idp));
1955 silc_buffer_free(idp);
1956 if (!cmd_ident && completion)
1957 completion(client, conn, SILC_STATUS_ERR_RESOURCE_LIMIT, NULL, context);
1959 if (server && cmd_ident)
1960 server->internal.resolve_cmd_ident = cmd_ident;
1962 silc_client_unref_server(client, conn, server);
1967 /************************** Server Entry Routines ***************************/
1969 /* Add new server entry */
1971 SilcServerEntry silc_client_add_server(SilcClient client,
1972 SilcClientConnection conn,
1973 const char *server_name,
1974 const char *server_info,
1975 SilcServerID *server_id)
1977 SilcServerEntry server_entry;
1978 char *server_namec = NULL;
1983 SILC_LOG_DEBUG(("Adding new server %s", server_name));
1985 server_entry = silc_calloc(1, sizeof(*server_entry));
1989 silc_rwlock_alloc(&server_entry->internal.lock);
1990 silc_atomic_init8(&server_entry->internal.refcnt, 0);
1991 server_entry->id = *server_id;
1993 server_entry->server_name = strdup(server_name);
1995 server_entry->server_info = strdup(server_info);
1997 /* Normalize server name */
1999 server_namec = silc_identifier_check(server_name, strlen(server_name),
2000 SILC_STRING_UTF8, 256, NULL);
2001 if (!server_namec) {
2002 silc_free(server_entry->server_name);
2003 silc_free(server_entry->server_info);
2004 silc_free(server_entry);
2009 silc_mutex_lock(conn->internal->lock);
2011 /* Add server to cache */
2012 if (!silc_idcache_add(conn->internal->server_cache, server_namec,
2013 &server_entry->id, server_entry)) {
2014 silc_free(server_namec);
2015 silc_free(server_entry->server_name);
2016 silc_free(server_entry->server_info);
2017 silc_free(server_entry);
2018 silc_mutex_unlock(conn->internal->lock);
2022 silc_mutex_unlock(conn->internal->lock);
2023 silc_client_ref_server(client, conn, server_entry);
2025 SILC_LOG_DEBUG(("Added %p", server_entry));
2027 return server_entry;
2030 /* Removes server from the cache by the server entry. */
2032 SilcBool silc_client_del_server(SilcClient client, SilcClientConnection conn,
2033 SilcServerEntry server)
2035 SilcIDCacheEntry id_cache;
2036 SilcBool ret = TRUE;
2042 if (silc_atomic_sub_int8(&server->internal.refcnt, 1) > 0)
2045 SILC_LOG_DEBUG(("Deleting server %p", server));
2047 silc_mutex_lock(conn->internal->lock);
2048 if (silc_idcache_find_by_context(conn->internal->server_cache, server,
2050 namec = id_cache->name;
2051 ret = silc_idcache_del_by_context(conn->internal->server_cache,
2055 silc_mutex_unlock(conn->internal->lock);
2057 silc_free(server->server_name);
2058 silc_free(server->server_info);
2059 if (server->public_key)
2060 silc_pkcs_public_key_free(server->public_key);
2061 silc_atomic_uninit8(&server->internal.refcnt);
2062 silc_rwlock_free(server->internal.lock);
2068 /* Updates the `server_entry' with the new information sent as argument. */
2070 void silc_client_update_server(SilcClient client,
2071 SilcClientConnection conn,
2072 SilcServerEntry server_entry,
2073 const char *server_name,
2074 const char *server_info)
2076 char *server_namec = NULL;
2078 SILC_LOG_DEBUG(("Updating server %p", server_entry));
2081 (!server_entry->server_name ||
2082 !silc_utf8_strcasecmp(server_entry->server_name, server_name))) {
2084 server_namec = silc_identifier_check(server_name, strlen(server_name),
2085 SILC_STRING_UTF8, 256, NULL);
2089 silc_free(server_entry->server_name);
2090 server_entry->server_name = strdup(server_name);
2091 if (!server_entry->server_name)
2094 /* Update cache entry */
2095 silc_mutex_lock(conn->internal->lock);
2096 silc_idcache_update_by_context(conn->internal->server_cache, server_entry,
2097 NULL, server_namec, TRUE);
2098 silc_mutex_unlock(conn->internal->lock);
2102 (!server_entry->server_info ||
2103 !silc_utf8_strcasecmp(server_entry->server_info, server_info))) {
2104 silc_free(server_entry->server_info);
2105 server_entry->server_info = strdup(server_info);
2111 void silc_client_lock_server(SilcServerEntry server_entry)
2113 silc_rwlock_rdlock(server_entry->internal.lock);
2118 void silc_client_unlock_server(SilcServerEntry server_entry)
2120 silc_rwlock_unlock(server_entry->internal.lock);
2123 /* Take reference of server entry */
2125 SilcServerEntry silc_client_ref_server(SilcClient client,
2126 SilcClientConnection conn,
2127 SilcServerEntry server_entry)
2129 silc_atomic_add_int8(&server_entry->internal.refcnt, 1);
2130 SILC_LOG_DEBUG(("Server %p refcnt %d->%d", server_entry,
2131 silc_atomic_get_int8(&server_entry->internal.refcnt) - 1,
2132 silc_atomic_get_int8(&server_entry->internal.refcnt)));
2133 return server_entry;
2136 /* Release reference of server entry */
2138 void silc_client_unref_server(SilcClient client, SilcClientConnection conn,
2139 SilcServerEntry server_entry)
2142 SILC_LOG_DEBUG(("Server %p refcnt %d->%d", server_entry,
2143 silc_atomic_get_int8(&server_entry->internal.refcnt),
2144 silc_atomic_get_int8(&server_entry->internal.refcnt)
2146 silc_client_del_server(client, conn, server_entry);
2150 /* Free server entry list */
2152 void silc_client_list_free_servers(SilcClient client,
2153 SilcClientConnection conn,
2154 SilcDList server_list)
2156 SilcServerEntry server_entry;
2159 silc_dlist_start(server_list);
2160 while ((server_entry = silc_dlist_get(server_list)))
2161 silc_client_unref_server(client, conn, server_entry);
2163 silc_dlist_uninit(server_list);