5 Author: Pekka Riikonen <priikone@silcnet.org>
7 Copyright (C) 2001 - 2007 Pekka Riikonen
9 This program is free software; you can redistribute it and/or modify
10 it under the terms of the GNU General Public License as published by
11 the Free Software Foundation; version 2 of the License.
13 This program is distributed in the hope that it will be useful,
14 but WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 GNU General Public License for more details.
22 #include "silcclient.h"
23 #include "client_internal.h"
25 /************************ Client Searching Locally **************************/
27 /* Finds entry for client by the client's ID. Returns the entry or NULL
28 if the entry was not found. */
30 SilcClientEntry silc_client_get_client_by_id(SilcClient client,
31 SilcClientConnection conn,
32 SilcClientID *client_id)
34 SilcIDCacheEntry id_cache;
35 SilcClientEntry client_entry;
37 if (!client || !conn || !client_id)
40 SILC_LOG_DEBUG(("Finding client by ID (%s)",
41 silc_id_render(client_id, SILC_ID_CLIENT)));
43 silc_mutex_lock(conn->internal->lock);
45 /* Find ID from cache */
46 if (!silc_idcache_find_by_id_one(conn->internal->client_cache, client_id,
48 silc_mutex_unlock(conn->internal->lock);
52 client_entry = id_cache->context;
55 silc_client_ref_client(client, conn, client_entry);
56 silc_mutex_unlock(conn->internal->lock);
58 SILC_LOG_DEBUG(("Found"));
63 /* Finds clients by nickname from local cache. */
65 SilcDList silc_client_get_clients_local_ext(SilcClient client,
66 SilcClientConnection conn,
71 SilcIDCacheEntry id_cache;
74 SilcClientEntry entry;
75 char nick[128 + 1], *nicknamec, *parsed = NULL, *format = NULL;
78 if (!client || !conn || !nickname)
81 /* Get nickname from nickname@server string */
82 silc_parse_userfqdn(nickname, nick, sizeof(nick), server, sizeof(server));
84 /* Parse nickname in case it is formatted */
85 if (!silc_client_nickname_parse(client, conn, (char *)nick, &parsed))
88 if (!get_all && parsed)
89 format = (char *)nick;
91 parsed = silc_memdup(nick, strlen(nick));
96 SILC_LOG_DEBUG(("Find clients by nickname %s", parsed));
98 /* Normalize nickname for search */
99 nicknamec = silc_identifier_check(parsed, strlen(parsed),
100 SILC_STRING_UTF8, 128, NULL);
106 clients = silc_dlist_init();
108 silc_free(nicknamec);
113 silc_mutex_lock(conn->internal->lock);
115 /* Find from cache */
116 silc_list_init(list, struct SilcIDCacheEntryStruct, next);
117 if (!silc_idcache_find_by_name(conn->internal->client_cache, nicknamec,
119 silc_mutex_unlock(conn->internal->lock);
120 silc_free(nicknamec);
122 silc_dlist_uninit(clients);
125 silc_list_start(list);
127 if (!format && get_all) {
128 /* Take all without any further checking */
129 while ((id_cache = silc_list_get(list))) {
130 entry = id_cache->context;
131 if (!get_valid || entry->internal.valid) {
132 silc_client_ref_client(client, conn, id_cache->context);
133 silc_dlist_add(clients, id_cache->context);
137 /* Check multiple cache entries for exact match */
138 while ((id_cache = silc_list_get(list))) {
139 entry = id_cache->context;
141 /* If server was provided, find entries that either have no server
142 set or have the same server. Ignore those that have different
144 if (server[0] && entry->server &&
145 !silc_utf8_strcasecmp(entry->server, server))
148 if (silc_utf8_strcasecmp(entry->nickname,
149 format ? format : parsed) &&
150 (!get_valid || entry->internal.valid)) {
151 silc_client_ref_client(client, conn, entry);
152 silc_dlist_add(clients, entry);
154 /* If format is NULL, we find one exact match with the base
155 nickname (parsed). */
162 silc_mutex_unlock(conn->internal->lock);
164 silc_free(nicknamec);
167 if (!silc_dlist_count(clients)) {
168 silc_dlist_uninit(clients);
172 SILC_LOG_DEBUG(("Found %d clients", silc_dlist_count(clients)));
174 silc_dlist_start(clients);
178 /* Finds clients by nickname from local cache. */
180 SilcDList silc_client_get_clients_local(SilcClient client,
181 SilcClientConnection conn,
182 const char *nickname,
185 return silc_client_get_clients_local_ext(client, conn, nickname, return_all,
189 /********************** Client Resolving from Server ************************/
191 /* Resolving context */
194 SilcGetClientCallback completion;
196 SilcClientEntry client_entry;
197 } *SilcClientGetClientInternal;
199 /* Resolving command callback */
201 static SilcBool silc_client_get_clients_cb(SilcClient client,
202 SilcClientConnection conn,
209 SilcClientGetClientInternal i = context;
210 SilcClientEntry client_entry;
212 if (error != SILC_STATUS_OK) {
213 SILC_LOG_DEBUG(("Resolving failed: %s", silc_get_status_message(error)));
215 if (i->client_entry) {
216 i->client_entry->internal.resolve_cmd_ident = 0;
217 silc_client_unref_client(client, conn, i->client_entry);
221 i->completion(client, conn, error, NULL, i->context);
225 /* Add the returned client to list */
227 client_entry = va_arg(ap, SilcClientEntry);
228 silc_client_ref_client(client, conn, client_entry);
229 silc_dlist_add(i->clients, client_entry);
230 client_entry->internal.resolve_cmd_ident = 0;
233 if (status == SILC_STATUS_OK || status == SILC_STATUS_LIST_END) {
234 /* Deliver the clients to the caller */
236 SILC_LOG_DEBUG(("Resolved %d clients", silc_dlist_count(i->clients)));
238 if (i->client_entry) {
239 i->client_entry->internal.resolve_cmd_ident = 0;
240 silc_client_unref_client(client, conn, i->client_entry);
243 silc_dlist_start(i->clients);
244 i->completion(client, conn, SILC_STATUS_OK, i->clients, i->context);
252 silc_client_list_free(client, conn, i->clients);
257 /* Resolves client information from server by the client ID. */
260 silc_client_get_client_by_id_resolve(SilcClient client,
261 SilcClientConnection conn,
262 SilcClientID *client_id,
263 SilcBuffer attributes,
264 SilcGetClientCallback completion,
267 SilcClientGetClientInternal i;
268 SilcClientEntry client_entry;
270 SilcUInt16 cmd_ident;
272 if (!client || !conn | !client_id) {
273 SILC_LOG_ERROR(("Missing arguments to "
274 "silc_client_get_clients_by_id_resolve call"));
278 SILC_LOG_DEBUG(("Resolve client by ID (%s)",
279 silc_id_render(client_id, SILC_ID_CLIENT)));
281 i = silc_calloc(1, sizeof(*i));
284 i->completion = completion;
285 i->context = context;
286 i->clients = silc_dlist_init();
292 /* Attach to resolving, if on going */
293 client_entry = silc_client_get_client_by_id(client, conn, client_id);
294 if (client_entry && client_entry->internal.resolve_cmd_ident) {
295 SILC_LOG_DEBUG(("Attach to existing resolving"));
296 silc_client_unref_client(client, conn, client_entry);
297 silc_client_command_pending(conn, SILC_COMMAND_NONE,
298 client_entry->internal.resolve_cmd_ident,
299 silc_client_get_clients_cb, i);
300 return client_entry->internal.resolve_cmd_ident;
303 /* Send the command */
304 idp = silc_id_payload_encode(client_id, SILC_ID_CLIENT);
305 cmd_ident = silc_client_command_send(client, conn, SILC_COMMAND_WHOIS,
306 silc_client_get_clients_cb, i,
307 2, 3, silc_buffer_datalen(attributes),
308 4, silc_buffer_datalen(idp));
309 if (!cmd_ident && completion)
310 completion(client, conn, SILC_STATUS_ERR_RESOURCE_LIMIT, NULL, context);
312 if (client_entry && cmd_ident) {
313 client_entry->internal.resolve_cmd_ident = cmd_ident;
314 i->client_entry = client_entry;
316 silc_client_unref_client(client, conn, client_entry);
319 silc_buffer_free(idp);
324 /* Finds client entry or entries by the `nickname' and `server'. The
325 completion callback will be called when the client entries has been
326 found. Used internally by the library. */
328 static SilcUInt16 silc_client_get_clients_i(SilcClient client,
329 SilcClientConnection conn,
331 const char *nickname,
333 SilcBuffer attributes,
334 SilcGetClientCallback completion,
337 SilcClientGetClientInternal i;
338 char nick[128 + 1], serv[256 + 1], userhost[768 + 1], *parsed = NULL;
341 SILC_LOG_DEBUG(("Resolve client by %s command",
342 silc_get_command_name(command)));
344 if (!client || !conn) {
345 SILC_LOG_ERROR(("Missing arguments to silc_client_get_clients call"));
348 if (!nickname && !attributes) {
349 SILC_LOG_ERROR(("Missing arguments to silc_client_get_clients call"));
353 /* Parse server name from the nickname if set */
354 if (silc_parse_userfqdn(nickname, nick, sizeof(nick),
355 serv, sizeof(serv)) == 2)
356 server = (const char *)serv;
357 nickname = (const char *)nick;
359 /* Parse nickname in case it is formatted */
360 if (silc_client_nickname_parse(client, conn, (char *)nickname, &parsed))
361 nickname = (const char *)parsed;
363 i = silc_calloc(1, sizeof(*i));
368 i->clients = silc_dlist_init();
374 i->completion = completion;
375 i->context = context;
377 memset(userhost, 0, sizeof(userhost));
378 if (nickname && server) {
379 len = strlen(nickname) + strlen(server) + 3;
380 silc_strncat(userhost, len, nickname, strlen(nickname));
381 silc_strncat(userhost, len, "@", 1);
382 silc_strncat(userhost, len, server, strlen(server));
383 } else if (nickname) {
384 silc_strncat(userhost, sizeof(userhost) - 1, nickname, strlen(nickname));
388 /* Send the command */
389 if (command == SILC_COMMAND_IDENTIFY)
390 return silc_client_command_send(client, conn, SILC_COMMAND_IDENTIFY,
391 silc_client_get_clients_cb, i,
392 1, 1, userhost, strlen(userhost));
393 return silc_client_command_send(client, conn, SILC_COMMAND_WHOIS,
394 silc_client_get_clients_cb, i,
395 2, 1, userhost, strlen(userhost),
396 3, silc_buffer_datalen(attributes));
399 /* Get clients from server with IDENTIFY command */
401 SilcUInt16 silc_client_get_clients(SilcClient client,
402 SilcClientConnection conn,
403 const char *nickname,
405 SilcGetClientCallback completion,
408 return silc_client_get_clients_i(client, conn, SILC_COMMAND_IDENTIFY,
409 nickname, server, NULL,
410 completion, context);
413 /* Get clients from server with WHOIS command */
415 SilcUInt16 silc_client_get_clients_whois(SilcClient client,
416 SilcClientConnection conn,
417 const char *nickname,
419 SilcBuffer attributes,
420 SilcGetClientCallback completion,
423 return silc_client_get_clients_i(client, conn, SILC_COMMAND_WHOIS,
424 nickname, server, attributes,
425 completion, context);
428 /* ID list resolving context */
430 SilcGetClientCallback completion;
432 SilcBuffer client_id_list;
433 SilcUInt32 list_count;
434 } *GetClientsByListInternal;
436 static SilcBool silc_client_get_clients_list_cb(SilcClient client,
437 SilcClientConnection conn,
444 GetClientsByListInternal i = context;
445 SilcClientEntry client_entry;
451 /* Process the list after all replies have been received */
452 if (status != SILC_STATUS_OK && !SILC_STATUS_IS_ERROR(status) &&
453 status != SILC_STATUS_LIST_END)
456 SILC_LOG_DEBUG(("Resolved all clients"));
458 clients = silc_dlist_init();
460 status = SILC_STATUS_ERR_RESOURCE_LIMIT;
464 for (c = 0; c < i->list_count; c++) {
466 SILC_GET16_MSB(idp_len, i->client_id_list->data + 2);
468 if (!silc_id_payload_parse_id(i->client_id_list->data, idp_len, &id)) {
469 status = SILC_STATUS_ERR_BAD_CLIENT_ID;
473 /* Get client entry */
474 client_entry = silc_client_get_client_by_id(client, conn, &id.u.client_id);
476 silc_dlist_add(clients, client_entry);
478 if (!silc_buffer_pull(i->client_id_list, idp_len)) {
479 status = SILC_STATUS_ERR_BAD_CLIENT_ID;
484 silc_dlist_start(clients);
485 status = SILC_STATUS_OK;
487 i->completion(client, conn, status, clients, i->context);
490 if (status != SILC_STATUS_OK && i->completion)
491 i->completion(client, conn, status, NULL, i->context);
493 silc_client_list_free(client, conn, clients);
494 silc_buffer_free(i->client_id_list);
500 /* Gets client entries by the list of client ID's `client_id_list'. This
501 always resolves those client ID's it does not know yet from the server
502 so this function might take a while. The `client_id_list' is a list
503 of ID Payloads added one after other. JOIN command reply and USERS
504 command reply for example returns this sort of list. The `completion'
505 will be called after the entries are available. */
507 SilcUInt16 silc_client_get_clients_by_list(SilcClient client,
508 SilcClientConnection conn,
509 SilcUInt32 list_count,
510 SilcBuffer client_id_list,
511 SilcGetClientCallback completion,
514 GetClientsByListInternal in;
515 SilcClientEntry entry;
516 unsigned char **res_argv = NULL;
517 SilcUInt32 *res_argv_lens = NULL, *res_argv_types = NULL, res_argc = 0;
518 SilcUInt16 idp_len, cmd_ident;
523 SILC_LOG_DEBUG(("Resolve clients from Client ID list"));
525 if (!client || !conn || !client_id_list)
528 in = silc_calloc(1, sizeof(*in));
531 in->completion = completion;
532 in->context = context;
533 in->list_count = list_count;
534 in->client_id_list = silc_buffer_copy(client_id_list);
535 if (!in->client_id_list)
538 for (i = 0; i < list_count; i++) {
540 SILC_GET16_MSB(idp_len, client_id_list->data + 2);
542 if (!silc_id_payload_parse_id(client_id_list->data, idp_len, &id))
545 /* Check if we have this client cached already. If we don't have the
546 entry or it has incomplete info, then resolve it from the server. */
547 entry = silc_client_get_client_by_id(client, conn, &id.u.client_id);
548 if (!entry || !entry->nickname[0] || !entry->username[0] ||
551 res_argv = silc_calloc(list_count, sizeof(*res_argv));
552 res_argv_lens = silc_calloc(list_count, sizeof(*res_argv_lens));
553 res_argv_types = silc_calloc(list_count, sizeof(*res_argv_types));
554 if (!res_argv || !res_argv_lens || !res_argv_types) {
555 silc_client_unref_client(client, conn, entry);
560 res_argv[res_argc] = client_id_list->data;
561 res_argv_lens[res_argc] = idp_len;
562 res_argv_types[res_argc] = res_argc + 4;
565 silc_client_unref_client(client, conn, entry);
567 if (!silc_buffer_pull(client_id_list, idp_len))
570 silc_buffer_start(client_id_list);
572 /* Query the unknown client information from server */
574 cmd_ident = silc_client_command_send_argv(client,
575 conn, SILC_COMMAND_WHOIS,
576 silc_client_get_clients_list_cb,
577 in, res_argc, res_argv,
581 silc_free(res_argv_lens);
582 silc_free(res_argv_types);
586 /* We have the clients in cache, get them and call the completion */
587 silc_client_get_clients_list_cb(client, conn, SILC_COMMAND_WHOIS,
588 SILC_STATUS_OK, SILC_STATUS_OK, in, tmp);
592 silc_buffer_free(in->client_id_list);
595 silc_free(res_argv_lens);
596 silc_free(res_argv_types);
603 SilcClientConnection conn;
604 SilcChannelID channel_id;
605 SilcGetClientCallback completion;
608 } *GetClientsByChannelInternal;
610 SILC_CLIENT_CMD_FUNC(get_clients_by_channel_cb)
612 GetClientsByChannelInternal i = context;
613 SilcClientEntry *clients = NULL;
614 SilcUInt32 clients_count = 0;
615 SilcBool found = FALSE;
616 SilcChannelEntry channel;
617 SilcHashTableList htl;
626 channel = silc_client_get_channel_by_id(i->client, i->conn, &i->channel_id);
627 if (channel && !silc_hash_table_count(channel->user_list)) {
628 clients = silc_calloc(silc_hash_table_count(channel->user_list),
630 silc_hash_table_list(channel->user_list, &htl);
631 while (silc_hash_table_get(&htl, NULL, (void *)&chu))
632 clients[clients_count++] = chu->client;
633 silc_hash_table_list_reset(&htl);
638 i->completion(i->client, i->conn, clients, clients_count, i->context);
641 i->completion(i->client, i->conn, NULL, 0, i->context);
647 /* Gets client entries by the channel entry indicated by `channel'. Thus,
648 it resolves the clients currently on that channel. */
650 void silc_client_get_clients_by_channel(SilcClient client,
651 SilcClientConnection conn,
652 SilcChannelEntry channel,
653 SilcGetClientCallback completion,
656 GetClientsByChannelInternal in;
657 SilcHashTableList htl;
659 SilcClientEntry entry;
660 unsigned char **res_argv = NULL;
661 SilcUInt32 *res_argv_lens = NULL, *res_argv_types = NULL, res_argc = 0;
663 SilcBool wait_res = FALSE;
665 assert(client && conn && channel);
667 SILC_LOG_DEBUG(("Start"));
669 in = silc_calloc(1, sizeof(*in));
672 in->channel_id = *channel->id;
673 in->completion = completion;
674 in->context = context;
676 /* If user list does not exist, send USERS command. */
677 if (!channel->user_list || !silc_hash_table_count(channel->user_list)) {
678 SILC_LOG_DEBUG(("Sending USERS"));
679 silc_client_command_register(client, SILC_COMMAND_USERS, NULL, NULL,
680 silc_client_command_reply_users_i, 0,
682 silc_client_command_send(client, conn, SILC_COMMAND_USERS,
683 conn->cmd_ident, 1, 2, channel->channel_name,
684 strlen(channel->channel_name));
685 silc_client_command_pending(conn, SILC_COMMAND_USERS, conn->cmd_ident,
686 silc_client_command_get_clients_by_channel_cb,
691 silc_hash_table_list(channel->user_list, &htl);
692 while (silc_hash_table_get(&htl, NULL, (void *)&chu)) {
695 /* If the entry has incomplete info, then resolve it from the server. */
696 if (!entry->nickname[0] || !entry->realname) {
697 if (entry->status & SILC_CLIENT_STATUS_RESOLVING) {
698 /* Attach to this resolving and wait until it finishes */
699 silc_client_command_pending(
700 conn, SILC_COMMAND_NONE,
701 entry->resolve_cmd_ident,
702 silc_client_command_get_clients_by_channel_cb,
708 entry->status |= SILC_CLIENT_STATUS_RESOLVING;
709 entry->resolve_cmd_ident = conn->cmd_ident + 1;
711 idp = silc_id_payload_encode(entry->id, SILC_ID_CLIENT);
713 /* No we don't have it, query it from the server. Assemble argument
714 table that will be sent for the WHOIS command later. */
715 res_argv = silc_realloc(res_argv, sizeof(*res_argv) *
717 res_argv_lens = silc_realloc(res_argv_lens, sizeof(*res_argv_lens) *
719 res_argv_types = silc_realloc(res_argv_types, sizeof(*res_argv_types) *
721 res_argv[res_argc] = silc_memdup(idp->data, idp->len);
722 res_argv_lens[res_argc] = idp->len;
723 res_argv_types[res_argc] = res_argc + 4;
726 silc_buffer_free(idp);
729 silc_hash_table_list_reset(&htl);
731 /* Query the client information from server if the list included clients
732 that we don't know about. */
736 /* Send the WHOIS command to server */
737 res_cmd = silc_command_payload_encode(SILC_COMMAND_WHOIS,
738 res_argc, res_argv, res_argv_lens,
739 res_argv_types, ++conn->cmd_ident);
740 silc_client_packet_send(client, conn->sock, SILC_PACKET_COMMAND,
741 NULL, 0, NULL, NULL, res_cmd->data, res_cmd->len,
744 /* Register our own command reply for this command */
745 silc_client_command_register(client, SILC_COMMAND_WHOIS, NULL, NULL,
746 silc_client_command_reply_whois_i, 0,
749 /* Process the applications request after reply has been received */
750 silc_client_command_pending(
751 conn, SILC_COMMAND_WHOIS, conn->cmd_ident,
752 silc_client_command_get_clients_by_channel_cb,
756 silc_buffer_free(res_cmd);
758 silc_free(res_argv_lens);
759 silc_free(res_argv_types);
766 /* We have the clients in cache, get them and call the completion */
767 silc_client_command_get_clients_by_channel_cb((void *)in, NULL);
772 /************************** Client Entry Routines ***************************/
774 /* Creates new client entry and adds it to the ID cache. Returns pointer
777 SilcClientEntry silc_client_add_client(SilcClient client,
778 SilcClientConnection conn,
779 char *nickname, char *username,
780 char *userinfo, SilcClientID *id,
783 SilcClientEntry client_entry;
784 char *nick = NULL, parsed[128 + 1];
786 SILC_LOG_DEBUG(("Adding new client entry"));
788 /* Save the client infos */
789 client_entry = silc_calloc(1, sizeof(*client_entry));
793 silc_rwlock_alloc(&client_entry->internal.lock);
794 silc_atomic_init8(&client_entry->internal.refcnt, 0);
795 client_entry->id = *id;
796 client_entry->mode = mode;
797 client_entry->realname = userinfo ? strdup(userinfo) : NULL;
799 silc_parse_userfqdn(nickname, parsed, sizeof(parsed),
800 client_entry->server, sizeof(client_entry->server));
801 if (nickname && client->internal->params->full_nicknames)
802 silc_snprintf(client_entry->nickname, sizeof(client_entry->nickname),
805 silc_snprintf(client_entry->nickname, sizeof(client_entry->nickname),
808 silc_parse_userfqdn(username, client_entry->username,
809 sizeof(client_entry->username),
810 client_entry->hostname,
811 sizeof(client_entry->hostname));
813 client_entry->channels = silc_hash_table_alloc(1, silc_hash_ptr, NULL, NULL,
814 NULL, NULL, NULL, TRUE);
815 if (!client_entry->channels) {
816 silc_free(client_entry->realname);
817 silc_free(client_entry);
821 /* Normalize nickname */
822 if (client_entry->nickname[0]) {
823 nick = silc_identifier_check(parsed, strlen(parsed),
824 SILC_STRING_UTF8, 128, NULL);
826 silc_free(client_entry->realname);
827 silc_hash_table_free(client_entry->channels);
828 silc_free(client_entry);
833 silc_mutex_lock(conn->internal->lock);
835 /* Add client to cache, the normalized nickname is saved to cache */
836 if (!silc_idcache_add(conn->internal->client_cache, nick,
837 &client_entry->id, client_entry)) {
839 silc_free(client_entry->realname);
840 silc_hash_table_free(client_entry->channels);
841 silc_free(client_entry);
842 silc_mutex_unlock(conn->internal->lock);
846 client_entry->nickname_normalized = nick;
848 silc_mutex_unlock(conn->internal->lock);
849 silc_client_ref_client(client, conn, client_entry);
851 /* Format the nickname */
852 silc_client_nickname_format(client, conn, client_entry, FALSE);
854 if (client_entry->nickname[0])
855 client_entry->internal.valid = TRUE;
857 SILC_LOG_DEBUG(("Added %p", client_entry));
862 /* Updates the `client_entry' with the new information sent as argument.
863 This handles entry locking internally. */
865 void silc_client_update_client(SilcClient client,
866 SilcClientConnection conn,
867 SilcClientEntry client_entry,
868 const char *nickname,
869 const char *username,
870 const char *userinfo,
873 char *nick = NULL, parsed[128 + 1];
875 SILC_LOG_DEBUG(("Update client entry"));
877 silc_rwlock_wrlock(client_entry->internal.lock);
879 if (!client_entry->realname && userinfo)
880 client_entry->realname = strdup(userinfo);
882 if ((!client_entry->username[0] || !client_entry->hostname[0]) && username)
883 silc_parse_userfqdn(username, client_entry->username,
884 sizeof(client_entry->username),
885 client_entry->hostname,
886 sizeof(client_entry->username));
888 if (!client_entry->nickname[0] && nickname) {
889 silc_parse_userfqdn(nickname, parsed, sizeof(parsed),
890 client_entry->server, sizeof(client_entry->server));
891 if (client->internal->params->full_nicknames)
892 silc_snprintf(client_entry->nickname, sizeof(client_entry->nickname),
895 silc_snprintf(client_entry->nickname, sizeof(client_entry->nickname),
898 /* Normalize nickname */
899 nick = silc_identifier_check(parsed, strlen(parsed),
900 SILC_STRING_UTF8, 128, NULL);
902 silc_rwlock_unlock(client_entry->internal.lock);
906 /* Format nickname */
907 silc_client_nickname_format(client, conn, client_entry,
908 client_entry == conn->local_entry);
910 /* Update cache entry */
911 silc_mutex_lock(conn->internal->lock);
912 silc_idcache_update_by_context(conn->internal->client_cache,
913 client_entry, NULL, nick, TRUE);
914 silc_mutex_unlock(conn->internal->lock);
915 client_entry->nickname_normalized = nick;
916 client_entry->internal.valid = TRUE;
918 client_entry->mode = mode;
920 silc_rwlock_unlock(client_entry->internal.lock);
923 /* Change a client's nickname. Must be called with `client_entry' locked. */
925 SilcBool silc_client_change_nickname(SilcClient client,
926 SilcClientConnection conn,
927 SilcClientEntry client_entry,
928 const char *new_nick,
929 SilcClientID *new_id,
930 const unsigned char *idp,
935 SILC_LOG_DEBUG(("Change nickname %s to %s", client_entry->nickname,
938 /* Normalize nickname */
939 tmp = silc_identifier_check(new_nick, strlen(new_nick),
940 SILC_STRING_UTF8, 128, NULL);
944 /* Update the client entry */
945 silc_mutex_lock(conn->internal->lock);
946 if (!silc_idcache_update_by_context(conn->internal->client_cache,
947 client_entry, new_id, tmp, TRUE)) {
949 silc_mutex_unlock(conn->internal->lock);
952 silc_mutex_unlock(conn->internal->lock);
954 memset(client_entry->nickname, 0, sizeof(client_entry->nickname));
955 memcpy(client_entry->nickname, new_nick, strlen(new_nick));
956 client_entry->nickname_normalized = tmp;
957 silc_client_nickname_format(client, conn, client_entry,
958 client_entry == conn->local_entry);
960 /* For my client entry, update ID and set new ID to packet stream */
961 if (client_entry == conn->local_entry) {
962 if (idp && idp_len) {
963 silc_buffer_enlarge(conn->internal->local_idp, idp_len);
964 silc_buffer_put(conn->internal->local_idp, idp, idp_len);
967 silc_packet_set_ids(conn->stream, SILC_ID_CLIENT, conn->local_id,
971 client_entry->internal.valid = TRUE;
975 /* Deletes the client entry and frees all memory. */
977 void silc_client_del_client_entry(SilcClient client,
978 SilcClientConnection conn,
979 SilcClientEntry client_entry)
981 silc_free(client_entry->realname);
982 silc_free(client_entry->nickname_normalized);
983 silc_free(client_entry->internal.key);
984 if (client_entry->public_key)
985 silc_pkcs_public_key_free(client_entry->public_key);
986 silc_hash_table_free(client_entry->channels);
987 if (client_entry->internal.send_key)
988 silc_cipher_free(client_entry->internal.send_key);
989 if (client_entry->internal.receive_key)
990 silc_cipher_free(client_entry->internal.receive_key);
991 if (client_entry->internal.hmac_send)
992 silc_hmac_free(client_entry->internal.hmac_send);
993 if (client_entry->internal.hmac_receive)
994 silc_hmac_free(client_entry->internal.hmac_receive);
995 silc_client_ftp_session_free_client(client, client_entry);
996 if (client_entry->internal.ke)
997 silc_client_abort_key_agreement(client, conn, client_entry);
998 silc_atomic_uninit8(&client_entry->internal.refcnt);
999 silc_rwlock_free(client_entry->internal.lock);
1000 silc_free(client_entry);
1003 /* Removes client from the cache by the client entry. */
1005 SilcBool silc_client_del_client(SilcClient client, SilcClientConnection conn,
1006 SilcClientEntry client_entry)
1013 if (silc_atomic_sub_int8(&client_entry->internal.refcnt, 1) > 0)
1016 SILC_LOG_DEBUG(("Deleting client %p", client_entry));
1018 silc_mutex_lock(conn->internal->lock);
1019 ret = silc_idcache_del_by_context(conn->internal->client_cache,
1020 client_entry, NULL);
1021 silc_mutex_unlock(conn->internal->lock);
1024 /* Remove from channels */
1025 silc_client_remove_from_channels(client, conn, client_entry);
1027 /* Free the client entry data */
1028 silc_client_del_client_entry(client, conn, client_entry);
1034 /* Internal routine used to find client by ID and if not found this creates
1035 new client entry and returns it. */
1037 SilcClientEntry silc_client_get_client(SilcClient client,
1038 SilcClientConnection conn,
1039 SilcClientID *client_id)
1041 SilcClientEntry client_entry;
1043 client_entry = silc_client_get_client_by_id(client, conn, client_id);
1044 if (!client_entry) {
1045 client_entry = silc_client_add_client(client, conn, NULL, NULL, NULL,
1049 silc_client_ref_client(client, conn, client_entry);
1052 return client_entry;
1057 void silc_client_lock_client(SilcClientEntry client_entry)
1059 silc_rwlock_rdlock(client_entry->internal.lock);
1064 void silc_client_unlock_client(SilcClientEntry client_entry)
1066 silc_rwlock_unlock(client_entry->internal.lock);
1069 /* Take reference of client entry */
1071 SilcClientEntry silc_client_ref_client(SilcClient client,
1072 SilcClientConnection conn,
1073 SilcClientEntry client_entry)
1075 silc_atomic_add_int8(&client_entry->internal.refcnt, 1);
1076 SILC_LOG_DEBUG(("Client %p refcnt %d->%d", client_entry,
1077 silc_atomic_get_int8(&client_entry->internal.refcnt) - 1,
1078 silc_atomic_get_int8(&client_entry->internal.refcnt)));
1079 return client_entry;
1082 /* Release reference of client entry */
1084 void silc_client_unref_client(SilcClient client, SilcClientConnection conn,
1085 SilcClientEntry client_entry)
1088 SILC_LOG_DEBUG(("Client %p refcnt %d->%d", client_entry,
1089 silc_atomic_get_int8(&client_entry->internal.refcnt),
1090 silc_atomic_get_int8(&client_entry->internal.refcnt) - 1));
1091 silc_client_del_client(client, conn, client_entry);
1095 /* Free client entry list */
1097 void silc_client_list_free(SilcClient client, SilcClientConnection conn,
1098 SilcDList client_list)
1100 SilcClientEntry client_entry;
1103 silc_dlist_start(client_list);
1104 while ((client_entry = silc_dlist_get(client_list)))
1105 silc_client_unref_client(client, conn, client_entry);
1107 silc_dlist_uninit(client_list);
1111 /* Formats the nickname of the client specified by the `client_entry'.
1112 If the format is specified by the application this will format the
1113 nickname and replace the old nickname in the client entry. If the
1114 format string is not specified then this function has no effect.
1115 Returns the client entry that was formatted. */
1117 SilcClientEntry silc_client_nickname_format(SilcClient client,
1118 SilcClientConnection conn,
1119 SilcClientEntry client_entry,
1123 char newnick[128 + 1];
1124 int i, off = 0, len;
1126 SilcClientEntry entry, unformatted = NULL;
1127 SilcBool formatted = FALSE;
1129 if (!client->internal->params->nickname_format[0])
1130 return client_entry;
1131 if (!client_entry->nickname[0])
1134 SILC_LOG_DEBUG(("Format nickname"));
1136 /* Get all clients with same nickname. Do not perform the formatting
1137 if there aren't any clients with same nickname unless the application
1138 is forcing us to do so. */
1139 clients = silc_client_get_clients_local_ext(client, conn,
1140 client_entry->nickname,
1144 if (silc_dlist_count(clients) == 1 && !priority &&
1145 !client->internal->params->nickname_force_format) {
1146 silc_client_list_free(client, conn, clients);
1147 return client_entry;
1150 /* Is the requested client formatted already */
1151 if (client_entry->nickname_normalized &&
1152 !silc_utf8_strcasecmp(client_entry->nickname,
1153 client_entry->nickname_normalized))
1156 if (client->internal->params->nickname_force_format)
1159 /* Find unformatted client entry */
1160 while ((entry = silc_dlist_get(clients))) {
1161 if (!entry->internal.valid)
1163 if (entry == client_entry)
1165 if (silc_utf8_strcasecmp(entry->nickname, entry->nickname_normalized)) {
1166 unformatted = entry;
1171 /* If there are no other unformatted clients and the requested client is
1172 unformatted, just return it. */
1173 if (!unformatted && !formatted) {
1174 silc_client_list_free(client, conn, clients);
1175 return client_entry;
1178 /* If priority formatting then the requested client will get the
1179 unformatted nickname, and the unformatted client will get a new
1180 formatted nickname. */
1183 /* Simply change the client's nickname to unformatted */
1184 if (!silc_client_nickname_parse(client, conn, client_entry->nickname,
1188 silc_snprintf(client_entry->nickname, sizeof(client_entry->nickname),
1194 /* There was no other unformatted client */
1195 silc_client_list_free(client, conn, clients);
1196 return client_entry;
1199 /* Now format the previously unformatted client */
1200 client_entry = unformatted;
1204 /* If already formatted just return it */
1206 silc_client_list_free(client, conn, clients);
1207 return client_entry;
1210 memset(newnick, 0, sizeof(newnick));
1211 cp = client->internal->params->nickname_format;
1221 if (!client_entry->nickname[0])
1223 len = strlen(client_entry->nickname);
1224 memcpy(&newnick[off], client_entry->nickname, len);
1228 /* Stripped hostname */
1229 if (!client_entry->hostname[0])
1231 len = strcspn(client_entry->hostname, ".");
1232 i = strcspn(client_entry->hostname, "-");
1235 memcpy(&newnick[off], client_entry->hostname, len);
1240 if (!client_entry->hostname[0])
1242 len = strlen(client_entry->hostname);
1243 memcpy(&newnick[off], client_entry->hostname, len);
1247 /* Ascending number */
1252 if (silc_dlist_count(clients) == 1)
1255 silc_dlist_start(clients);
1256 while ((entry = silc_dlist_get(clients))) {
1257 if (!silc_utf8_strncasecmp(entry->nickname, newnick, off))
1259 if (strlen(entry->nickname) <= off)
1261 num = atoi(&entry->nickname[off]);
1266 memset(tmp, 0, sizeof(tmp));
1267 silc_snprintf(tmp, sizeof(tmp) - 1, "%d", ++max);
1269 memcpy(&newnick[off], tmp, len);
1274 /* Some other character in the string */
1275 memcpy(&newnick[off], cp, 1);
1284 memset(client_entry->nickname, 0, sizeof(client_entry->nickname));
1285 memcpy(client_entry->nickname, newnick, strlen(newnick));
1286 silc_client_list_free(client, conn, clients);
1288 return client_entry;
1291 /* Parses nickname according to nickname format string */
1293 SilcBool silc_client_nickname_parse(SilcClient client,
1294 SilcClientConnection conn,
1298 char *cp, s = 0, e = 0, *nick;
1302 if (!client->internal->params->nickname_format[0]) {
1307 if (!nickname || !nickname[0])
1310 cp = client->internal->params->nickname_format;
1328 /* Get separator character */
1341 /* Parse the nickname */
1345 if (strchr(nickname, s))
1346 nick = strchr(nickname, s) + 1;
1348 if (strchr(nick, e))
1349 len = strchr(nick, e) - nick;
1353 *ret_nick = silc_memdup(nick, len);
1357 SILC_LOG_DEBUG(("Parsed nickname: %s", *ret_nick));
1362 /************************ Channel Searching Locally *************************/
1364 /* Finds entry for channel by the channel name. Returns the entry or NULL
1365 if the entry was not found. It is found only if the client is joined
1368 SilcChannelEntry silc_client_get_channel(SilcClient client,
1369 SilcClientConnection conn,
1373 SilcIDCacheEntry id_cache;
1374 SilcChannelEntry entry = NULL;
1375 char chname[256 + 1], server[256 + 1];
1377 if (!client || !conn || !channel)
1380 SILC_LOG_DEBUG(("Find channel %s", channel));
1382 /* Parse server name from channel name */
1383 silc_parse_userfqdn(channel, chname, sizeof(chname), server, sizeof(server));
1385 /* Normalize name for search */
1386 channel = silc_channel_name_check(chname, strlen(chname), SILC_STRING_UTF8,
1391 silc_mutex_lock(conn->internal->lock);
1393 if (!silc_idcache_find_by_name(conn->internal->channel_cache, channel,
1395 silc_mutex_unlock(conn->internal->lock);
1400 /* If server name was specified with channel name, find the correct
1401 channel entry with the server name. There can only be one channel
1402 with same name on same server. */
1403 silc_list_start(list);
1405 while ((id_cache = silc_list_get(list))) {
1406 entry = id_cache->context;
1407 if (!entry->server[0])
1409 if (silc_utf8_strcasecmp(entry->server, server))
1413 /* Get first channel without server name specified or one with our
1414 current server connection name */
1415 while ((id_cache = silc_list_get(list))) {
1416 entry = id_cache->context;
1417 if (!entry->server[0])
1419 if (silc_utf8_strcasecmp(entry->server, conn->remote_host))
1425 silc_mutex_unlock(conn->internal->lock);
1430 SILC_LOG_DEBUG(("Found channel %s%s%s", entry->channel_name,
1431 entry->server[0] ? "@" : "", entry->server));
1434 silc_client_ref_channel(client, conn, entry);
1435 silc_mutex_unlock(conn->internal->lock);
1442 /* Finds entry for channel by the channel ID. Returns the entry or NULL
1443 if the entry was not found. It is found only if the client is joined
1446 SilcChannelEntry silc_client_get_channel_by_id(SilcClient client,
1447 SilcClientConnection conn,
1448 SilcChannelID *channel_id)
1450 SilcIDCacheEntry id_cache;
1451 SilcChannelEntry entry;
1453 if (!client || !conn || !channel_id)
1456 SILC_LOG_DEBUG(("Find channel by id %s",
1457 silc_id_render(channel_id, SILC_ID_CHANNEL)));
1459 silc_mutex_lock(conn->internal->lock);
1461 if (!silc_idcache_find_by_id_one(conn->internal->channel_cache, channel_id,
1463 silc_mutex_unlock(conn->internal->lock);
1467 SILC_LOG_DEBUG(("Found"));
1469 entry = id_cache->context;
1472 silc_client_ref_channel(client, conn, entry);
1473 silc_mutex_unlock(conn->internal->lock);
1478 /********************** Channel Resolving from Server ***********************/
1480 /* Channel resolving context */
1483 SilcGetChannelCallback completion;
1485 } *SilcClientGetChannelInternal;
1487 /* Resolving command callback */
1489 static SilcBool silc_client_get_channel_cb(SilcClient client,
1490 SilcClientConnection conn,
1491 SilcCommand command,
1497 SilcClientGetChannelInternal i = context;
1498 SilcChannelEntry entry;
1500 if (error != SILC_STATUS_OK) {
1501 SILC_LOG_DEBUG(("Resolving failed: %s", silc_get_status_message(error)));
1503 i->completion(client, conn, error, NULL, i->context);
1507 /* Add the returned channel to list */
1508 if (i->completion) {
1509 entry = va_arg(ap, SilcChannelEntry);
1510 silc_client_ref_channel(client, conn, entry);
1511 silc_dlist_add(i->channels, entry);
1514 if (status == SILC_STATUS_OK || status == SILC_STATUS_LIST_END) {
1515 /* Deliver the channels to the caller */
1516 if (i->completion) {
1517 SILC_LOG_DEBUG(("Resolved %d channels", silc_dlist_count(i->channels)));
1518 silc_dlist_start(i->channels);
1519 i->completion(client, conn, SILC_STATUS_OK, i->channels, i->context);
1527 silc_client_list_free_channels(client, conn, i->channels);
1532 /* Resolves channel entry from the server by the channel name. */
1534 void silc_client_get_channel_resolve(SilcClient client,
1535 SilcClientConnection conn,
1537 SilcGetChannelCallback completion,
1540 SilcClientGetChannelInternal i;
1542 if (!client || !conn || !channel_name || !completion)
1545 SILC_LOG_DEBUG(("Resolve channel %s", channel_name));
1547 i = silc_calloc(1, sizeof(*i));
1550 i->completion = completion;
1551 i->context = context;
1552 i->channels = silc_dlist_init();
1558 /* Send the command */
1559 if (!silc_client_command_send(client, conn, SILC_COMMAND_IDENTIFY,
1560 silc_client_get_channel_cb, i, 1,
1561 3, channel_name, strlen(channel_name))) {
1563 completion(client, conn, SILC_STATUS_ERR_RESOURCE_LIMIT, NULL, context);
1567 /* Resolves channel information from the server by the channel ID. */
1570 silc_client_get_channel_by_id_resolve(SilcClient client,
1571 SilcClientConnection conn,
1572 SilcChannelID *channel_id,
1573 SilcGetChannelCallback completion,
1576 SilcClientGetChannelInternal i;
1578 SilcUInt16 cmd_ident;
1580 if (!client || !conn || !channel_id || !completion)
1583 SILC_LOG_DEBUG(("Resolve channel by id %s",
1584 silc_id_render(channel_id, SILC_ID_CHANNEL)));
1586 i = silc_calloc(1, sizeof(*i));
1589 i->completion = completion;
1590 i->context = context;
1591 i->channels = silc_dlist_init();
1597 /* Send the command */
1598 idp = silc_id_payload_encode(channel_id, SILC_ID_CHANNEL);
1599 cmd_ident = silc_client_command_send(client, conn, SILC_COMMAND_IDENTIFY,
1600 silc_client_get_channel_cb, i, 1,
1601 5, silc_buffer_datalen(idp));
1602 silc_buffer_free(idp);
1603 if (!cmd_ident && completion)
1604 completion(client, conn, SILC_STATUS_ERR_RESOURCE_LIMIT, NULL, context);
1609 /************************* Channel Entry Routines ***************************/
1611 /* Add new channel entry to the ID Cache */
1613 SilcChannelEntry silc_client_add_channel(SilcClient client,
1614 SilcClientConnection conn,
1615 const char *channel_name,
1617 SilcChannelID *channel_id)
1619 SilcChannelEntry channel;
1620 char *channel_namec, name[256 + 1];
1622 SILC_LOG_DEBUG(("Adding channel %s", channel_name));
1624 channel = silc_calloc(1, sizeof(*channel));
1628 silc_rwlock_alloc(&channel->internal.lock);
1629 silc_atomic_init16(&channel->internal.refcnt, 0);
1630 channel->id = *channel_id;
1631 channel->mode = mode;
1633 silc_parse_userfqdn(channel_name, name, sizeof(name),
1634 channel->server, sizeof(channel->server));
1635 if (client->internal->params->full_channel_names)
1636 channel->channel_name = strdup(channel_name);
1638 channel->channel_name = strdup(name);
1640 if (!channel->channel_name) {
1641 silc_rwlock_free(channel->internal.lock);
1642 silc_atomic_uninit16(&channel->internal.refcnt);
1647 channel->user_list = silc_hash_table_alloc(1, silc_hash_ptr, NULL, NULL,
1648 NULL, NULL, NULL, TRUE);
1649 if (!channel->user_list) {
1650 silc_rwlock_free(channel->internal.lock);
1651 silc_atomic_uninit16(&channel->internal.refcnt);
1652 silc_free(channel->channel_name);
1657 /* Normalize channel name */
1658 channel_namec = silc_channel_name_check(name, strlen(name),
1659 SILC_STRING_UTF8, 256, NULL);
1660 if (!channel_namec) {
1661 silc_rwlock_free(channel->internal.lock);
1662 silc_atomic_uninit16(&channel->internal.refcnt);
1663 silc_free(channel->channel_name);
1664 silc_hash_table_free(channel->user_list);
1669 silc_mutex_lock(conn->internal->lock);
1671 /* Add channel to cache, the normalized channel name is saved to cache */
1672 if (!silc_idcache_add(conn->internal->channel_cache, channel_namec,
1673 &channel->id, channel)) {
1674 silc_rwlock_free(channel->internal.lock);
1675 silc_atomic_uninit16(&channel->internal.refcnt);
1676 silc_free(channel_namec);
1677 silc_free(channel->channel_name);
1678 silc_hash_table_free(channel->user_list);
1680 silc_mutex_unlock(conn->internal->lock);
1684 silc_mutex_unlock(conn->internal->lock);
1685 silc_client_ref_channel(client, conn, channel);
1687 SILC_LOG_DEBUG(("Added %p", channel));
1692 /* Removes channel from the cache by the channel entry. */
1694 SilcBool silc_client_del_channel(SilcClient client, SilcClientConnection conn,
1695 SilcChannelEntry channel)
1697 SilcIDCacheEntry id_cache;
1698 SilcBool ret = TRUE;
1706 if (silc_atomic_sub_int16(&channel->internal.refcnt, 1) > 0)
1709 SILC_LOG_DEBUG(("Deleting channel %p", channel));
1711 silc_mutex_lock(conn->internal->lock);
1712 if (silc_idcache_find_by_context(conn->internal->channel_cache, channel,
1714 namec = id_cache->name;
1715 ret = silc_idcache_del_by_context(conn->internal->channel_cache,
1719 silc_mutex_unlock(conn->internal->lock);
1724 silc_client_empty_channel(client, conn, channel);
1725 silc_hash_table_free(channel->user_list);
1726 silc_free(channel->channel_name);
1727 silc_free(channel->topic);
1728 if (channel->founder_key)
1729 silc_pkcs_public_key_free(channel->founder_key);
1730 if (channel->internal.send_key)
1731 silc_cipher_free(channel->internal.send_key);
1732 if (channel->internal.receive_key)
1733 silc_cipher_free(channel->internal.receive_key);
1734 if (channel->internal.hmac)
1735 silc_hmac_free(channel->internal.hmac);
1736 if (channel->internal.old_channel_keys) {
1737 silc_dlist_start(channel->internal.old_channel_keys);
1738 while ((key = silc_dlist_get(channel->internal.old_channel_keys)))
1739 silc_cipher_free(key);
1740 silc_dlist_uninit(channel->internal.old_channel_keys);
1742 if (channel->internal.old_hmacs) {
1743 silc_dlist_start(channel->internal.old_hmacs);
1744 while ((hmac = silc_dlist_get(channel->internal.old_hmacs)))
1745 silc_hmac_free(hmac);
1746 silc_dlist_uninit(channel->internal.old_hmacs);
1748 if (channel->channel_pubkeys)
1749 silc_argument_list_free(channel->channel_pubkeys,
1750 SILC_ARGUMENT_PUBLIC_KEY);
1751 silc_client_del_channel_private_keys(client, conn, channel);
1752 silc_atomic_uninit16(&channel->internal.refcnt);
1753 silc_rwlock_free(channel->internal.lock);
1754 silc_schedule_task_del_by_context(conn->client->schedule, channel);
1760 /* Replaces the channel ID of the `channel' to `new_id'. Returns FALSE
1761 if the ID could not be changed. This handles entry locking internally. */
1763 SilcBool silc_client_replace_channel_id(SilcClient client,
1764 SilcClientConnection conn,
1765 SilcChannelEntry channel,
1766 SilcChannelID *new_id)
1768 SilcBool ret = FALSE;
1773 SILC_LOG_DEBUG(("Old Channel ID id(%s)",
1774 silc_id_render(&channel->id, SILC_ID_CHANNEL)));
1775 SILC_LOG_DEBUG(("New Channel ID id(%s)",
1776 silc_id_render(new_id, SILC_ID_CHANNEL)));
1779 silc_rwlock_wrlock(channel->internal.lock);
1780 silc_mutex_lock(conn->internal->lock);
1781 silc_idcache_update_by_context(conn->internal->channel_cache, channel,
1782 new_id, NULL, FALSE);
1783 silc_mutex_unlock(conn->internal->lock);
1784 silc_rwlock_unlock(channel->internal.lock);
1791 void silc_client_lock_channel(SilcChannelEntry channel_entry)
1793 silc_rwlock_rdlock(channel_entry->internal.lock);
1796 /* Unlock channel */
1798 void silc_client_unlock_channel(SilcChannelEntry channel_entry)
1800 silc_rwlock_unlock(channel_entry->internal.lock);
1803 /* Take reference of channel entry */
1805 SilcChannelEntry silc_client_ref_channel(SilcClient client,
1806 SilcClientConnection conn,
1807 SilcChannelEntry channel_entry)
1809 silc_atomic_add_int16(&channel_entry->internal.refcnt, 1);
1810 SILC_LOG_DEBUG(("Channel %p refcnt %d->%d", channel_entry,
1811 silc_atomic_get_int16(&channel_entry->internal.refcnt) - 1,
1812 silc_atomic_get_int16(&channel_entry->internal.refcnt)));
1813 return channel_entry;
1816 /* Release reference of channel entry */
1818 void silc_client_unref_channel(SilcClient client, SilcClientConnection conn,
1819 SilcChannelEntry channel_entry)
1821 if (channel_entry) {
1822 SILC_LOG_DEBUG(("Channel %p refcnt %d->%d", channel_entry,
1823 silc_atomic_get_int16(&channel_entry->internal.refcnt),
1824 silc_atomic_get_int16(&channel_entry->internal.refcnt)
1826 silc_client_del_channel(client, conn, channel_entry);
1830 /* Free channel entry list */
1832 void silc_client_list_free_channels(SilcClient client,
1833 SilcClientConnection conn,
1834 SilcDList channel_list)
1836 SilcChannelEntry channel_entry;
1839 silc_dlist_start(channel_list);
1840 while ((channel_entry = silc_dlist_get(channel_list)))
1841 silc_client_unref_channel(client, conn, channel_entry);
1843 silc_dlist_uninit(channel_list);
1847 /************************* Server Searching Locally *************************/
1849 /* Finds entry for server by the server name. */
1851 SilcServerEntry silc_client_get_server(SilcClient client,
1852 SilcClientConnection conn,
1855 SilcIDCacheEntry id_cache;
1856 SilcServerEntry entry;
1858 if (!client || !conn || !server_name)
1861 SILC_LOG_DEBUG(("Find server by name %s", server_name));
1863 /* Normalize server name for search */
1864 server_name = silc_identifier_check(server_name, strlen(server_name),
1865 SILC_STRING_UTF8, 256, NULL);
1869 silc_mutex_lock(conn->internal->lock);
1871 if (!silc_idcache_find_by_name_one(conn->internal->server_cache,
1872 server_name, &id_cache)) {
1873 silc_free(server_name);
1874 silc_mutex_unlock(conn->internal->lock);
1878 SILC_LOG_DEBUG(("Found"));
1881 entry = id_cache->context;
1882 silc_client_ref_server(client, conn, entry);
1884 silc_mutex_unlock(conn->internal->lock);
1886 silc_free(server_name);
1891 /* Finds entry for server by the server ID. */
1893 SilcServerEntry silc_client_get_server_by_id(SilcClient client,
1894 SilcClientConnection conn,
1895 SilcServerID *server_id)
1897 SilcIDCacheEntry id_cache;
1898 SilcServerEntry entry;
1900 if (!client || !conn || !server_id)
1903 SILC_LOG_DEBUG(("Find server by id %s",
1904 silc_id_render(server_id, SILC_ID_SERVER)));
1906 silc_mutex_lock(conn->internal->lock);
1908 if (!silc_idcache_find_by_id_one(conn->internal->server_cache,
1909 server_id, &id_cache)) {
1910 silc_mutex_unlock(conn->internal->lock);
1914 SILC_LOG_DEBUG(("Found"));
1917 entry = id_cache->context;
1918 silc_client_ref_server(client, conn, entry);
1920 silc_mutex_unlock(conn->internal->lock);
1925 /*********************** Server Resolving from Server ***********************/
1927 /* Resolving context */
1930 SilcGetServerCallback completion;
1932 } *SilcClientGetServerInternal;
1934 /* Resolving command callback */
1936 static SilcBool silc_client_get_server_cb(SilcClient client,
1937 SilcClientConnection conn,
1938 SilcCommand command,
1944 SilcClientGetServerInternal i = context;
1945 SilcServerEntry server;
1947 if (error != SILC_STATUS_OK) {
1948 SILC_LOG_DEBUG(("Resolving failed: %s", silc_get_status_message(error)));
1950 i->completion(client, conn, error, NULL, i->context);
1954 /* Add the returned servers to list */
1955 if (i->completion) {
1956 server = va_arg(ap, SilcServerEntry);
1957 silc_client_ref_server(client, conn, server);
1958 silc_dlist_add(i->servers, server);
1959 server->internal.resolve_cmd_ident = 0;
1962 if (status == SILC_STATUS_OK || status == SILC_STATUS_LIST_END) {
1963 /* Deliver the servers to the caller */
1964 if (i->completion) {
1965 SILC_LOG_DEBUG(("Resolved %d servers", silc_dlist_count(i->servers)));
1966 silc_dlist_start(i->servers);
1967 i->completion(client, conn, SILC_STATUS_OK, i->servers, i->context);
1975 silc_client_list_free_servers(client, conn, i->servers);
1980 /* Resolve server by server ID */
1983 silc_client_get_server_by_id_resolve(SilcClient client,
1984 SilcClientConnection conn,
1985 SilcServerID *server_id,
1986 SilcGetServerCallback completion,
1989 SilcClientGetServerInternal i;
1990 SilcServerEntry server;
1992 SilcUInt16 cmd_ident;
1994 if (!client || !conn || !server_id || !completion)
1997 SILC_LOG_DEBUG(("Resolve server by id %s",
1998 silc_id_render(server_id, SILC_ID_SERVER)));
2000 i = silc_calloc(1, sizeof(*i));
2003 i->completion = completion;
2004 i->context = context;
2005 i->servers = silc_dlist_init();
2011 /* Attach to resolving, if on going */
2012 server = silc_client_get_server_by_id(client, conn, server_id);
2013 if (server && server->internal.resolve_cmd_ident) {
2014 SILC_LOG_DEBUG(("Attach to existing resolving"));
2015 silc_client_unref_server(client, conn, server);
2016 silc_client_command_pending(conn, SILC_COMMAND_NONE,
2017 server->internal.resolve_cmd_ident,
2018 silc_client_get_server_cb, i);
2019 return server->internal.resolve_cmd_ident;
2022 /* Send the command */
2023 idp = silc_id_payload_encode(server_id, SILC_ID_SERVER);
2024 cmd_ident = silc_client_command_send(client, conn, SILC_COMMAND_IDENTIFY,
2025 silc_client_get_server_cb, i, 1,
2026 5, silc_buffer_datalen(idp));
2027 silc_buffer_free(idp);
2028 if (!cmd_ident && completion)
2029 completion(client, conn, SILC_STATUS_ERR_RESOURCE_LIMIT, NULL, context);
2031 if (server && cmd_ident)
2032 server->internal.resolve_cmd_ident = cmd_ident;
2034 silc_client_unref_server(client, conn, server);
2039 /************************** Server Entry Routines ***************************/
2041 /* Add new server entry */
2043 SilcServerEntry silc_client_add_server(SilcClient client,
2044 SilcClientConnection conn,
2045 const char *server_name,
2046 const char *server_info,
2047 SilcServerID *server_id)
2049 SilcServerEntry server_entry;
2050 char *server_namec = NULL;
2055 SILC_LOG_DEBUG(("Adding new server %s", server_name));
2057 server_entry = silc_calloc(1, sizeof(*server_entry));
2061 silc_rwlock_alloc(&server_entry->internal.lock);
2062 silc_atomic_init8(&server_entry->internal.refcnt, 0);
2063 server_entry->id = *server_id;
2065 server_entry->server_name = strdup(server_name);
2067 server_entry->server_info = strdup(server_info);
2069 /* Normalize server name */
2071 server_namec = silc_identifier_check(server_name, strlen(server_name),
2072 SILC_STRING_UTF8, 256, NULL);
2073 if (!server_namec) {
2074 silc_free(server_entry->server_name);
2075 silc_free(server_entry->server_info);
2076 silc_free(server_entry);
2081 silc_mutex_lock(conn->internal->lock);
2083 /* Add server to cache */
2084 if (!silc_idcache_add(conn->internal->server_cache, server_namec,
2085 &server_entry->id, server_entry)) {
2086 silc_free(server_namec);
2087 silc_free(server_entry->server_name);
2088 silc_free(server_entry->server_info);
2089 silc_free(server_entry);
2090 silc_mutex_unlock(conn->internal->lock);
2094 silc_mutex_unlock(conn->internal->lock);
2095 silc_client_ref_server(client, conn, server_entry);
2097 SILC_LOG_DEBUG(("Added %p", server_entry));
2099 return server_entry;
2102 /* Removes server from the cache by the server entry. */
2104 SilcBool silc_client_del_server(SilcClient client, SilcClientConnection conn,
2105 SilcServerEntry server)
2107 SilcIDCacheEntry id_cache;
2108 SilcBool ret = TRUE;
2114 if (silc_atomic_sub_int8(&server->internal.refcnt, 1) > 0)
2117 SILC_LOG_DEBUG(("Deleting server %p", server));
2119 silc_mutex_lock(conn->internal->lock);
2120 if (silc_idcache_find_by_context(conn->internal->server_cache, server,
2122 namec = id_cache->name;
2123 ret = silc_idcache_del_by_context(conn->internal->server_cache,
2127 silc_mutex_unlock(conn->internal->lock);
2129 silc_free(server->server_name);
2130 silc_free(server->server_info);
2131 if (server->public_key)
2132 silc_pkcs_public_key_free(server->public_key);
2133 silc_atomic_uninit8(&server->internal.refcnt);
2134 silc_rwlock_free(server->internal.lock);
2140 /* Updates the `server_entry' with the new information sent as argument. */
2142 void silc_client_update_server(SilcClient client,
2143 SilcClientConnection conn,
2144 SilcServerEntry server_entry,
2145 const char *server_name,
2146 const char *server_info)
2148 char *server_namec = NULL;
2150 SILC_LOG_DEBUG(("Updating server %p", server_entry));
2153 (!server_entry->server_name ||
2154 !silc_utf8_strcasecmp(server_entry->server_name, server_name))) {
2156 server_namec = silc_identifier_check(server_name, strlen(server_name),
2157 SILC_STRING_UTF8, 256, NULL);
2161 silc_free(server_entry->server_name);
2162 server_entry->server_name = strdup(server_name);
2163 if (!server_entry->server_name)
2166 /* Update cache entry */
2167 silc_mutex_lock(conn->internal->lock);
2168 silc_idcache_update_by_context(conn->internal->server_cache, server_entry,
2169 NULL, server_namec, TRUE);
2170 silc_mutex_unlock(conn->internal->lock);
2174 (!server_entry->server_info ||
2175 !silc_utf8_strcasecmp(server_entry->server_info, server_info))) {
2176 silc_free(server_entry->server_info);
2177 server_entry->server_info = strdup(server_info);
2183 void silc_client_lock_server(SilcServerEntry server_entry)
2185 silc_rwlock_rdlock(server_entry->internal.lock);
2190 void silc_client_unlock_server(SilcServerEntry server_entry)
2192 silc_rwlock_unlock(server_entry->internal.lock);
2195 /* Take reference of server entry */
2197 SilcServerEntry silc_client_ref_server(SilcClient client,
2198 SilcClientConnection conn,
2199 SilcServerEntry server_entry)
2201 silc_atomic_add_int8(&server_entry->internal.refcnt, 1);
2202 SILC_LOG_DEBUG(("Server %p refcnt %d->%d", server_entry,
2203 silc_atomic_get_int8(&server_entry->internal.refcnt) - 1,
2204 silc_atomic_get_int8(&server_entry->internal.refcnt)));
2205 return server_entry;
2208 /* Release reference of server entry */
2210 void silc_client_unref_server(SilcClient client, SilcClientConnection conn,
2211 SilcServerEntry server_entry)
2214 SILC_LOG_DEBUG(("Server %p refcnt %d->%d", server_entry,
2215 silc_atomic_get_int8(&server_entry->internal.refcnt),
2216 silc_atomic_get_int8(&server_entry->internal.refcnt)
2218 silc_client_del_server(client, conn, server_entry);
2222 /* Free server entry list */
2224 void silc_client_list_free_servers(SilcClient client,
2225 SilcClientConnection conn,
2226 SilcDList server_list)
2228 SilcServerEntry server_entry;
2231 silc_dlist_start(server_list);
2232 while ((server_entry = silc_dlist_get(server_list)))
2233 silc_client_unref_server(client, conn, server_entry);
2235 silc_dlist_uninit(server_list);