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;
522 SILC_LOG_DEBUG(("Resolve clients from Client ID list"));
524 if (!client || !conn || !client_id_list)
527 in = silc_calloc(1, sizeof(*in));
530 in->completion = completion;
531 in->context = context;
532 in->list_count = list_count;
533 in->client_id_list = silc_buffer_copy(client_id_list);
534 if (!in->client_id_list)
537 for (i = 0; i < list_count; i++) {
539 SILC_GET16_MSB(idp_len, client_id_list->data + 2);
541 if (!silc_id_payload_parse_id(client_id_list->data, idp_len, &id))
544 /* Check if we have this client cached already. If we don't have the
545 entry or it has incomplete info, then resolve it from the server. */
546 entry = silc_client_get_client_by_id(client, conn, &id.u.client_id);
547 if (!entry || !entry->nickname[0] || !entry->username[0] ||
550 res_argv = silc_calloc(list_count, sizeof(*res_argv));
551 res_argv_lens = silc_calloc(list_count, sizeof(*res_argv_lens));
552 res_argv_types = silc_calloc(list_count, sizeof(*res_argv_types));
553 if (!res_argv || !res_argv_lens || !res_argv_types) {
554 silc_client_unref_client(client, conn, entry);
559 res_argv[res_argc] = client_id_list->data;
560 res_argv_lens[res_argc] = idp_len;
561 res_argv_types[res_argc] = res_argc + 4;
564 silc_client_unref_client(client, conn, entry);
566 if (!silc_buffer_pull(client_id_list, idp_len))
569 silc_buffer_start(client_id_list);
571 /* Query the unknown client information from server */
573 cmd_ident = silc_client_command_send_argv(client,
574 conn, SILC_COMMAND_WHOIS,
575 silc_client_get_clients_list_cb,
576 in, res_argc, res_argv,
580 silc_free(res_argv_lens);
581 silc_free(res_argv_types);
585 /* We have the clients in cache, get them and call the completion */
586 silc_client_get_clients_list_cb(client, conn, SILC_COMMAND_WHOIS,
587 SILC_STATUS_OK, SILC_STATUS_OK, in, NULL);
591 silc_buffer_free(in->client_id_list);
594 silc_free(res_argv_lens);
595 silc_free(res_argv_types);
602 SilcClientConnection conn;
603 SilcChannelID channel_id;
604 SilcGetClientCallback completion;
607 } *GetClientsByChannelInternal;
609 SILC_CLIENT_CMD_FUNC(get_clients_by_channel_cb)
611 GetClientsByChannelInternal i = context;
612 SilcClientEntry *clients = NULL;
613 SilcUInt32 clients_count = 0;
614 SilcBool found = FALSE;
615 SilcChannelEntry channel;
616 SilcHashTableList htl;
625 channel = silc_client_get_channel_by_id(i->client, i->conn, &i->channel_id);
626 if (channel && !silc_hash_table_count(channel->user_list)) {
627 clients = silc_calloc(silc_hash_table_count(channel->user_list),
629 silc_hash_table_list(channel->user_list, &htl);
630 while (silc_hash_table_get(&htl, NULL, (void *)&chu))
631 clients[clients_count++] = chu->client;
632 silc_hash_table_list_reset(&htl);
637 i->completion(i->client, i->conn, clients, clients_count, i->context);
640 i->completion(i->client, i->conn, NULL, 0, i->context);
646 /* Gets client entries by the channel entry indicated by `channel'. Thus,
647 it resolves the clients currently on that channel. */
649 void silc_client_get_clients_by_channel(SilcClient client,
650 SilcClientConnection conn,
651 SilcChannelEntry channel,
652 SilcGetClientCallback completion,
655 GetClientsByChannelInternal in;
656 SilcHashTableList htl;
658 SilcClientEntry entry;
659 unsigned char **res_argv = NULL;
660 SilcUInt32 *res_argv_lens = NULL, *res_argv_types = NULL, res_argc = 0;
662 SilcBool wait_res = FALSE;
664 assert(client && conn && channel);
666 SILC_LOG_DEBUG(("Start"));
668 in = silc_calloc(1, sizeof(*in));
671 in->channel_id = *channel->id;
672 in->completion = completion;
673 in->context = context;
675 /* If user list does not exist, send USERS command. */
676 if (!channel->user_list || !silc_hash_table_count(channel->user_list)) {
677 SILC_LOG_DEBUG(("Sending USERS"));
678 silc_client_command_register(client, SILC_COMMAND_USERS, NULL, NULL,
679 silc_client_command_reply_users_i, 0,
681 silc_client_command_send(client, conn, SILC_COMMAND_USERS,
682 conn->cmd_ident, 1, 2, channel->channel_name,
683 strlen(channel->channel_name));
684 silc_client_command_pending(conn, SILC_COMMAND_USERS, conn->cmd_ident,
685 silc_client_command_get_clients_by_channel_cb,
690 silc_hash_table_list(channel->user_list, &htl);
691 while (silc_hash_table_get(&htl, NULL, (void *)&chu)) {
694 /* If the entry has incomplete info, then resolve it from the server. */
695 if (!entry->nickname[0] || !entry->realname) {
696 if (entry->status & SILC_CLIENT_STATUS_RESOLVING) {
697 /* Attach to this resolving and wait until it finishes */
698 silc_client_command_pending(
699 conn, SILC_COMMAND_NONE,
700 entry->resolve_cmd_ident,
701 silc_client_command_get_clients_by_channel_cb,
707 entry->status |= SILC_CLIENT_STATUS_RESOLVING;
708 entry->resolve_cmd_ident = conn->cmd_ident + 1;
710 idp = silc_id_payload_encode(entry->id, SILC_ID_CLIENT);
712 /* No we don't have it, query it from the server. Assemble argument
713 table that will be sent for the WHOIS command later. */
714 res_argv = silc_realloc(res_argv, sizeof(*res_argv) *
716 res_argv_lens = silc_realloc(res_argv_lens, sizeof(*res_argv_lens) *
718 res_argv_types = silc_realloc(res_argv_types, sizeof(*res_argv_types) *
720 res_argv[res_argc] = silc_memdup(idp->data, idp->len);
721 res_argv_lens[res_argc] = idp->len;
722 res_argv_types[res_argc] = res_argc + 4;
725 silc_buffer_free(idp);
728 silc_hash_table_list_reset(&htl);
730 /* Query the client information from server if the list included clients
731 that we don't know about. */
735 /* Send the WHOIS command to server */
736 res_cmd = silc_command_payload_encode(SILC_COMMAND_WHOIS,
737 res_argc, res_argv, res_argv_lens,
738 res_argv_types, ++conn->cmd_ident);
739 silc_client_packet_send(client, conn->sock, SILC_PACKET_COMMAND,
740 NULL, 0, NULL, NULL, res_cmd->data, res_cmd->len,
743 /* Register our own command reply for this command */
744 silc_client_command_register(client, SILC_COMMAND_WHOIS, NULL, NULL,
745 silc_client_command_reply_whois_i, 0,
748 /* Process the applications request after reply has been received */
749 silc_client_command_pending(
750 conn, SILC_COMMAND_WHOIS, conn->cmd_ident,
751 silc_client_command_get_clients_by_channel_cb,
755 silc_buffer_free(res_cmd);
757 silc_free(res_argv_lens);
758 silc_free(res_argv_types);
765 /* We have the clients in cache, get them and call the completion */
766 silc_client_command_get_clients_by_channel_cb((void *)in, NULL);
771 /************************** Client Entry Routines ***************************/
773 /* Creates new client entry and adds it to the ID cache. Returns pointer
776 SilcClientEntry silc_client_add_client(SilcClient client,
777 SilcClientConnection conn,
778 char *nickname, char *username,
779 char *userinfo, SilcClientID *id,
782 SilcClientEntry client_entry;
783 char *nick = NULL, parsed[128 + 1];
785 SILC_LOG_DEBUG(("Adding new client entry"));
787 /* Save the client infos */
788 client_entry = silc_calloc(1, sizeof(*client_entry));
792 silc_rwlock_alloc(&client_entry->internal.lock);
793 silc_atomic_init16(&client_entry->internal.refcnt, 0);
794 client_entry->id = *id;
795 client_entry->mode = mode;
796 client_entry->realname = userinfo ? strdup(userinfo) : NULL;
798 silc_parse_userfqdn(nickname, parsed, sizeof(parsed),
799 client_entry->server, sizeof(client_entry->server));
800 if (nickname && client->internal->params->full_nicknames)
801 silc_snprintf(client_entry->nickname, sizeof(client_entry->nickname),
804 silc_snprintf(client_entry->nickname, sizeof(client_entry->nickname),
807 silc_parse_userfqdn(username, client_entry->username,
808 sizeof(client_entry->username),
809 client_entry->hostname,
810 sizeof(client_entry->hostname));
812 client_entry->channels = silc_hash_table_alloc(NULL, 1, silc_hash_ptr, NULL,
813 NULL, NULL, NULL, NULL, TRUE);
814 if (!client_entry->channels) {
815 silc_free(client_entry->realname);
816 silc_free(client_entry);
820 /* Normalize nickname */
821 if (client_entry->nickname[0]) {
822 nick = silc_identifier_check(parsed, strlen(parsed),
823 SILC_STRING_UTF8, 128, NULL);
825 silc_free(client_entry->realname);
826 silc_hash_table_free(client_entry->channels);
827 silc_free(client_entry);
832 silc_mutex_lock(conn->internal->lock);
834 /* Add client to cache, the normalized nickname is saved to cache */
835 if (!silc_idcache_add(conn->internal->client_cache, nick,
836 &client_entry->id, client_entry)) {
838 silc_free(client_entry->realname);
839 silc_hash_table_free(client_entry->channels);
840 silc_free(client_entry);
841 silc_mutex_unlock(conn->internal->lock);
845 client_entry->nickname_normalized = nick;
847 silc_mutex_unlock(conn->internal->lock);
848 silc_client_ref_client(client, conn, client_entry);
850 /* Format the nickname */
851 silc_client_nickname_format(client, conn, client_entry, FALSE);
853 if (client_entry->nickname[0])
854 client_entry->internal.valid = TRUE;
856 SILC_LOG_DEBUG(("Added %p", client_entry));
861 /* Updates the `client_entry' with the new information sent as argument.
862 This handles entry locking internally. */
864 void silc_client_update_client(SilcClient client,
865 SilcClientConnection conn,
866 SilcClientEntry client_entry,
867 const char *nickname,
868 const char *username,
869 const char *userinfo,
872 char *nick = NULL, parsed[128 + 1];
874 SILC_LOG_DEBUG(("Update client entry"));
876 silc_rwlock_wrlock(client_entry->internal.lock);
878 if (!client_entry->realname && userinfo)
879 client_entry->realname = strdup(userinfo);
881 if ((!client_entry->username[0] || !client_entry->hostname[0]) && username)
882 silc_parse_userfqdn(username, client_entry->username,
883 sizeof(client_entry->username),
884 client_entry->hostname,
885 sizeof(client_entry->username));
887 if (!client_entry->nickname[0] && nickname) {
888 silc_parse_userfqdn(nickname, parsed, sizeof(parsed),
889 client_entry->server, sizeof(client_entry->server));
890 if (client->internal->params->full_nicknames)
891 silc_snprintf(client_entry->nickname, sizeof(client_entry->nickname),
894 silc_snprintf(client_entry->nickname, sizeof(client_entry->nickname),
897 /* Normalize nickname */
898 nick = silc_identifier_check(parsed, strlen(parsed),
899 SILC_STRING_UTF8, 128, NULL);
901 silc_rwlock_unlock(client_entry->internal.lock);
905 /* Format nickname */
906 silc_client_nickname_format(client, conn, client_entry,
907 client_entry == conn->local_entry);
909 /* Update cache entry */
910 silc_mutex_lock(conn->internal->lock);
911 silc_idcache_update_by_context(conn->internal->client_cache,
912 client_entry, NULL, nick, TRUE);
913 silc_mutex_unlock(conn->internal->lock);
914 client_entry->nickname_normalized = nick;
915 client_entry->internal.valid = TRUE;
917 client_entry->mode = mode;
919 silc_rwlock_unlock(client_entry->internal.lock);
922 /* Change a client's nickname. Must be called with `client_entry' locked. */
924 SilcBool silc_client_change_nickname(SilcClient client,
925 SilcClientConnection conn,
926 SilcClientEntry client_entry,
927 const char *new_nick,
928 SilcClientID *new_id,
929 const unsigned char *idp,
934 SILC_LOG_DEBUG(("Change nickname %s to %s", client_entry->nickname,
937 /* Normalize nickname */
938 tmp = silc_identifier_check(new_nick, strlen(new_nick),
939 SILC_STRING_UTF8, 128, NULL);
943 /* Update the client entry */
944 silc_mutex_lock(conn->internal->lock);
945 if (!silc_idcache_update_by_context(conn->internal->client_cache,
946 client_entry, new_id, tmp, TRUE)) {
948 silc_mutex_unlock(conn->internal->lock);
951 silc_mutex_unlock(conn->internal->lock);
953 memset(client_entry->nickname, 0, sizeof(client_entry->nickname));
954 memcpy(client_entry->nickname, new_nick, strlen(new_nick));
955 client_entry->nickname_normalized = tmp;
956 silc_client_nickname_format(client, conn, client_entry,
957 client_entry == conn->local_entry);
959 /* For my client entry, update ID and set new ID to packet stream */
960 if (client_entry == conn->local_entry) {
961 if (idp && idp_len) {
962 silc_buffer_enlarge(conn->internal->local_idp, idp_len);
963 silc_buffer_put(conn->internal->local_idp, idp, idp_len);
966 silc_packet_set_ids(conn->stream, SILC_ID_CLIENT, conn->local_id,
970 client_entry->internal.valid = TRUE;
974 /* Deletes the client entry and frees all memory. */
976 void silc_client_del_client_entry(SilcClient client,
977 SilcClientConnection conn,
978 SilcClientEntry client_entry)
980 silc_free(client_entry->realname);
981 silc_free(client_entry->nickname_normalized);
982 silc_free(client_entry->internal.key);
983 if (client_entry->public_key)
984 silc_pkcs_public_key_free(client_entry->public_key);
985 silc_hash_table_free(client_entry->channels);
986 if (client_entry->internal.send_key)
987 silc_cipher_free(client_entry->internal.send_key);
988 if (client_entry->internal.receive_key)
989 silc_cipher_free(client_entry->internal.receive_key);
990 if (client_entry->internal.hmac_send)
991 silc_hmac_free(client_entry->internal.hmac_send);
992 if (client_entry->internal.hmac_receive)
993 silc_hmac_free(client_entry->internal.hmac_receive);
994 silc_client_ftp_session_free_client(client, client_entry);
995 if (client_entry->internal.ke)
996 silc_client_abort_key_agreement(client, conn, client_entry);
997 silc_atomic_uninit16(&client_entry->internal.refcnt);
998 silc_rwlock_free(client_entry->internal.lock);
999 silc_free(client_entry);
1002 /* Removes client from the cache by the client entry. */
1004 SilcBool silc_client_del_client(SilcClient client, SilcClientConnection conn,
1005 SilcClientEntry client_entry)
1012 if (silc_atomic_sub_int16(&client_entry->internal.refcnt, 1) > 0)
1015 SILC_LOG_DEBUG(("Deleting client %p", client_entry));
1017 silc_mutex_lock(conn->internal->lock);
1018 ret = silc_idcache_del_by_context(conn->internal->client_cache,
1019 client_entry, NULL);
1020 silc_mutex_unlock(conn->internal->lock);
1023 /* Remove from channels */
1024 silc_client_remove_from_channels(client, conn, client_entry);
1026 /* Free the client entry data */
1027 silc_client_del_client_entry(client, conn, client_entry);
1033 /* Internal routine used to find client by ID and if not found this creates
1034 new client entry and returns it. */
1036 SilcClientEntry silc_client_get_client(SilcClient client,
1037 SilcClientConnection conn,
1038 SilcClientID *client_id)
1040 SilcClientEntry client_entry;
1042 client_entry = silc_client_get_client_by_id(client, conn, client_id);
1043 if (!client_entry) {
1044 client_entry = silc_client_add_client(client, conn, NULL, NULL, NULL,
1048 silc_client_ref_client(client, conn, client_entry);
1051 return client_entry;
1056 void silc_client_lock_client(SilcClientEntry client_entry)
1058 silc_rwlock_rdlock(client_entry->internal.lock);
1063 void silc_client_unlock_client(SilcClientEntry client_entry)
1065 silc_rwlock_unlock(client_entry->internal.lock);
1068 /* Take reference of client entry */
1070 SilcClientEntry silc_client_ref_client(SilcClient client,
1071 SilcClientConnection conn,
1072 SilcClientEntry client_entry)
1074 silc_atomic_add_int16(&client_entry->internal.refcnt, 1);
1075 SILC_LOG_DEBUG(("Client %p refcnt %d->%d", client_entry,
1076 silc_atomic_get_int16(&client_entry->internal.refcnt) - 1,
1077 silc_atomic_get_int16(&client_entry->internal.refcnt)));
1078 return client_entry;
1081 /* Release reference of client entry */
1083 void silc_client_unref_client(SilcClient client, SilcClientConnection conn,
1084 SilcClientEntry client_entry)
1087 SILC_LOG_DEBUG(("Client %p refcnt %d->%d", client_entry,
1088 silc_atomic_get_int16(&client_entry->internal.refcnt),
1089 silc_atomic_get_int16(&client_entry->internal.refcnt) - 1));
1090 silc_client_del_client(client, conn, client_entry);
1094 /* Free client entry list */
1096 void silc_client_list_free(SilcClient client, SilcClientConnection conn,
1097 SilcDList client_list)
1099 SilcClientEntry client_entry;
1102 silc_dlist_start(client_list);
1103 while ((client_entry = silc_dlist_get(client_list)))
1104 silc_client_unref_client(client, conn, client_entry);
1106 silc_dlist_uninit(client_list);
1110 /* Formats the nickname of the client specified by the `client_entry'.
1111 If the format is specified by the application this will format the
1112 nickname and replace the old nickname in the client entry. If the
1113 format string is not specified then this function has no effect.
1114 Returns the client entry that was formatted. */
1116 SilcClientEntry silc_client_nickname_format(SilcClient client,
1117 SilcClientConnection conn,
1118 SilcClientEntry client_entry,
1122 char newnick[128 + 1];
1123 int i, off = 0, len;
1125 SilcClientEntry entry, unformatted = NULL;
1126 SilcBool formatted = FALSE;
1128 if (!client->internal->params->nickname_format[0])
1129 return client_entry;
1130 if (!client_entry->nickname[0])
1133 SILC_LOG_DEBUG(("Format nickname"));
1135 /* Get all clients with same nickname. Do not perform the formatting
1136 if there aren't any clients with same nickname unless the application
1137 is forcing us to do so. */
1138 clients = silc_client_get_clients_local_ext(client, conn,
1139 client_entry->nickname,
1143 if (silc_dlist_count(clients) == 1 && !priority &&
1144 !client->internal->params->nickname_force_format) {
1145 silc_client_list_free(client, conn, clients);
1146 return client_entry;
1149 /* Is the requested client formatted already */
1150 if (client_entry->nickname_normalized &&
1151 !silc_utf8_strcasecmp(client_entry->nickname,
1152 client_entry->nickname_normalized))
1155 if (client->internal->params->nickname_force_format)
1158 /* Find unformatted client entry */
1159 while ((entry = silc_dlist_get(clients))) {
1160 if (!entry->internal.valid)
1162 if (entry == client_entry)
1164 if (silc_utf8_strcasecmp(entry->nickname, entry->nickname_normalized)) {
1165 unformatted = entry;
1170 /* If there are no other unformatted clients and the requested client is
1171 unformatted, just return it. */
1172 if (!unformatted && !formatted) {
1173 silc_client_list_free(client, conn, clients);
1174 return client_entry;
1177 /* If priority formatting then the requested client will get the
1178 unformatted nickname, and the unformatted client will get a new
1179 formatted nickname. */
1182 /* Simply change the client's nickname to unformatted */
1183 if (!silc_client_nickname_parse(client, conn, client_entry->nickname,
1187 silc_snprintf(client_entry->nickname, sizeof(client_entry->nickname),
1193 /* There was no other unformatted client */
1194 silc_client_list_free(client, conn, clients);
1195 return client_entry;
1198 /* Now format the previously unformatted client */
1199 client_entry = unformatted;
1203 /* If already formatted just return it */
1205 silc_client_list_free(client, conn, clients);
1206 return client_entry;
1209 memset(newnick, 0, sizeof(newnick));
1210 cp = client->internal->params->nickname_format;
1220 if (!client_entry->nickname[0])
1222 len = strlen(client_entry->nickname);
1223 memcpy(&newnick[off], client_entry->nickname, len);
1227 /* Stripped hostname */
1228 if (!client_entry->hostname[0])
1230 len = strcspn(client_entry->hostname, ".");
1231 i = strcspn(client_entry->hostname, "-");
1234 memcpy(&newnick[off], client_entry->hostname, len);
1239 if (!client_entry->hostname[0])
1241 len = strlen(client_entry->hostname);
1242 memcpy(&newnick[off], client_entry->hostname, len);
1246 /* Ascending number */
1251 if (silc_dlist_count(clients) == 1)
1254 silc_dlist_start(clients);
1255 while ((entry = silc_dlist_get(clients))) {
1256 if (!silc_utf8_strncasecmp(entry->nickname, newnick, off))
1258 if (strlen(entry->nickname) <= off)
1260 num = atoi(&entry->nickname[off]);
1265 memset(tmp, 0, sizeof(tmp));
1266 silc_snprintf(tmp, sizeof(tmp) - 1, "%d", ++max);
1268 memcpy(&newnick[off], tmp, len);
1273 /* Some other character in the string */
1274 memcpy(&newnick[off], cp, 1);
1283 memcpy(client_entry->nickname, newnick, strlen(newnick));
1284 silc_client_list_free(client, conn, clients);
1286 return client_entry;
1289 /* Parses nickname according to nickname format string */
1291 SilcBool silc_client_nickname_parse(SilcClient client,
1292 SilcClientConnection conn,
1296 char *cp, s = 0, e = 0, *nick;
1300 if (!client->internal->params->nickname_format[0]) {
1305 if (!nickname || !nickname[0])
1308 cp = client->internal->params->nickname_format;
1326 /* Get separator character */
1339 /* Parse the nickname */
1343 if (strchr(nickname, s))
1344 nick = strchr(nickname, s) + 1;
1346 if (strchr(nick, e))
1347 len = strchr(nick, e) - nick;
1351 *ret_nick = silc_memdup(nick, len);
1355 SILC_LOG_DEBUG(("Parsed nickname: %s", *ret_nick));
1360 /************************ Channel Searching Locally *************************/
1362 /* Finds entry for channel by the channel name. Returns the entry or NULL
1363 if the entry was not found. It is found only if the client is joined
1366 SilcChannelEntry silc_client_get_channel(SilcClient client,
1367 SilcClientConnection conn,
1371 SilcIDCacheEntry id_cache;
1372 SilcChannelEntry entry = NULL;
1373 char chname[256 + 1], server[256 + 1];
1375 if (!client || !conn || !channel)
1378 SILC_LOG_DEBUG(("Find channel %s", channel));
1380 /* Parse server name from channel name */
1381 silc_parse_userfqdn(channel, chname, sizeof(chname), server, sizeof(server));
1383 /* Normalize name for search */
1384 channel = silc_channel_name_check(chname, strlen(chname), SILC_STRING_UTF8,
1389 silc_mutex_lock(conn->internal->lock);
1391 if (!silc_idcache_find_by_name(conn->internal->channel_cache, channel,
1393 silc_mutex_unlock(conn->internal->lock);
1398 /* If server name was specified with channel name, find the correct
1399 channel entry with the server name. There can only be one channel
1400 with same name on same server. */
1401 silc_list_start(list);
1403 while ((id_cache = silc_list_get(list))) {
1404 entry = id_cache->context;
1405 if (!entry->server[0])
1407 if (silc_utf8_strcasecmp(entry->server, server))
1411 /* Get first channel without server name specified or one with our
1412 current server connection name */
1413 while ((id_cache = silc_list_get(list))) {
1414 entry = id_cache->context;
1415 if (!entry->server[0])
1417 if (silc_utf8_strcasecmp(entry->server, conn->remote_host))
1423 silc_mutex_unlock(conn->internal->lock);
1428 SILC_LOG_DEBUG(("Found channel %s%s%s", entry->channel_name,
1429 entry->server[0] ? "@" : "", entry->server));
1432 silc_client_ref_channel(client, conn, entry);
1433 silc_mutex_unlock(conn->internal->lock);
1440 /* Finds entry for channel by the channel ID. Returns the entry or NULL
1441 if the entry was not found. It is found only if the client is joined
1444 SilcChannelEntry silc_client_get_channel_by_id(SilcClient client,
1445 SilcClientConnection conn,
1446 SilcChannelID *channel_id)
1448 SilcIDCacheEntry id_cache;
1449 SilcChannelEntry entry;
1451 if (!client || !conn || !channel_id)
1454 SILC_LOG_DEBUG(("Find channel by id %s",
1455 silc_id_render(channel_id, SILC_ID_CHANNEL)));
1457 silc_mutex_lock(conn->internal->lock);
1459 if (!silc_idcache_find_by_id_one(conn->internal->channel_cache, channel_id,
1461 silc_mutex_unlock(conn->internal->lock);
1465 SILC_LOG_DEBUG(("Found"));
1467 entry = id_cache->context;
1470 silc_client_ref_channel(client, conn, entry);
1471 silc_mutex_unlock(conn->internal->lock);
1476 /********************** Channel Resolving from Server ***********************/
1478 /* Channel resolving context */
1481 SilcGetChannelCallback completion;
1483 } *SilcClientGetChannelInternal;
1485 /* Resolving command callback */
1487 static SilcBool silc_client_get_channel_cb(SilcClient client,
1488 SilcClientConnection conn,
1489 SilcCommand command,
1495 SilcClientGetChannelInternal i = context;
1496 SilcChannelEntry entry;
1498 if (error != SILC_STATUS_OK) {
1499 SILC_LOG_DEBUG(("Resolving failed: %s", silc_get_status_message(error)));
1501 i->completion(client, conn, error, NULL, i->context);
1505 /* Add the returned channel to list */
1506 if (i->completion) {
1507 entry = va_arg(ap, SilcChannelEntry);
1508 silc_client_ref_channel(client, conn, entry);
1509 silc_dlist_add(i->channels, entry);
1512 if (status == SILC_STATUS_OK || status == SILC_STATUS_LIST_END) {
1513 /* Deliver the channels to the caller */
1514 if (i->completion) {
1515 SILC_LOG_DEBUG(("Resolved %d channels", silc_dlist_count(i->channels)));
1516 silc_dlist_start(i->channels);
1517 i->completion(client, conn, SILC_STATUS_OK, i->channels, i->context);
1525 silc_client_list_free_channels(client, conn, i->channels);
1530 /* Resolves channel entry from the server by the channel name. */
1532 void silc_client_get_channel_resolve(SilcClient client,
1533 SilcClientConnection conn,
1535 SilcGetChannelCallback completion,
1538 SilcClientGetChannelInternal i;
1540 if (!client || !conn || !channel_name || !completion)
1543 SILC_LOG_DEBUG(("Resolve channel %s", channel_name));
1545 i = silc_calloc(1, sizeof(*i));
1548 i->completion = completion;
1549 i->context = context;
1550 i->channels = silc_dlist_init();
1556 /* Send the command */
1557 if (!silc_client_command_send(client, conn, SILC_COMMAND_IDENTIFY,
1558 silc_client_get_channel_cb, i, 1,
1559 3, channel_name, strlen(channel_name))) {
1561 completion(client, conn, SILC_STATUS_ERR_RESOURCE_LIMIT, NULL, context);
1565 /* Resolves channel information from the server by the channel ID. */
1568 silc_client_get_channel_by_id_resolve(SilcClient client,
1569 SilcClientConnection conn,
1570 SilcChannelID *channel_id,
1571 SilcGetChannelCallback completion,
1574 SilcClientGetChannelInternal i;
1576 SilcUInt16 cmd_ident;
1578 if (!client || !conn || !channel_id || !completion)
1581 SILC_LOG_DEBUG(("Resolve channel by id %s",
1582 silc_id_render(channel_id, SILC_ID_CHANNEL)));
1584 i = silc_calloc(1, sizeof(*i));
1587 i->completion = completion;
1588 i->context = context;
1589 i->channels = silc_dlist_init();
1595 /* Send the command */
1596 idp = silc_id_payload_encode(channel_id, SILC_ID_CHANNEL);
1597 cmd_ident = silc_client_command_send(client, conn, SILC_COMMAND_IDENTIFY,
1598 silc_client_get_channel_cb, i, 1,
1599 5, silc_buffer_datalen(idp));
1600 silc_buffer_free(idp);
1601 if (!cmd_ident && completion)
1602 completion(client, conn, SILC_STATUS_ERR_RESOURCE_LIMIT, NULL, context);
1607 /************************* Channel Entry Routines ***************************/
1609 /* Add new channel entry to the ID Cache */
1611 SilcChannelEntry silc_client_add_channel(SilcClient client,
1612 SilcClientConnection conn,
1613 const char *channel_name,
1615 SilcChannelID *channel_id)
1617 SilcChannelEntry channel;
1618 char *channel_namec, name[256 + 1];
1620 SILC_LOG_DEBUG(("Adding channel %s", channel_name));
1622 channel = silc_calloc(1, sizeof(*channel));
1626 silc_rwlock_alloc(&channel->internal.lock);
1627 silc_atomic_init16(&channel->internal.refcnt, 0);
1628 channel->id = *channel_id;
1629 channel->mode = mode;
1631 silc_parse_userfqdn(channel_name, name, sizeof(name),
1632 channel->server, sizeof(channel->server));
1633 if (client->internal->params->full_channel_names)
1634 channel->channel_name = strdup(channel_name);
1636 channel->channel_name = strdup(name);
1638 if (!channel->channel_name) {
1639 silc_rwlock_free(channel->internal.lock);
1640 silc_atomic_uninit16(&channel->internal.refcnt);
1645 channel->user_list = silc_hash_table_alloc(NULL, 1, silc_hash_ptr, NULL, NULL,
1646 NULL, NULL, NULL, TRUE);
1647 if (!channel->user_list) {
1648 silc_rwlock_free(channel->internal.lock);
1649 silc_atomic_uninit16(&channel->internal.refcnt);
1650 silc_free(channel->channel_name);
1655 /* Normalize channel name */
1656 channel_namec = silc_channel_name_check(name, strlen(name),
1657 SILC_STRING_UTF8, 256, NULL);
1658 if (!channel_namec) {
1659 silc_rwlock_free(channel->internal.lock);
1660 silc_atomic_uninit16(&channel->internal.refcnt);
1661 silc_free(channel->channel_name);
1662 silc_hash_table_free(channel->user_list);
1667 silc_mutex_lock(conn->internal->lock);
1669 /* Add channel to cache, the normalized channel name is saved to cache */
1670 if (!silc_idcache_add(conn->internal->channel_cache, channel_namec,
1671 &channel->id, channel)) {
1672 silc_rwlock_free(channel->internal.lock);
1673 silc_atomic_uninit16(&channel->internal.refcnt);
1674 silc_free(channel_namec);
1675 silc_free(channel->channel_name);
1676 silc_hash_table_free(channel->user_list);
1678 silc_mutex_unlock(conn->internal->lock);
1682 silc_mutex_unlock(conn->internal->lock);
1683 silc_client_ref_channel(client, conn, channel);
1685 SILC_LOG_DEBUG(("Added %p", channel));
1690 /* Removes channel from the cache by the channel entry. */
1692 SilcBool silc_client_del_channel(SilcClient client, SilcClientConnection conn,
1693 SilcChannelEntry channel)
1695 SilcIDCacheEntry id_cache;
1696 SilcBool ret = TRUE;
1704 if (silc_atomic_sub_int16(&channel->internal.refcnt, 1) > 0)
1707 SILC_LOG_DEBUG(("Deleting channel %p", channel));
1709 silc_mutex_lock(conn->internal->lock);
1710 if (silc_idcache_find_by_context(conn->internal->channel_cache, channel,
1712 namec = id_cache->name;
1713 ret = silc_idcache_del_by_context(conn->internal->channel_cache,
1717 silc_mutex_unlock(conn->internal->lock);
1722 silc_client_empty_channel(client, conn, channel);
1723 silc_client_del_channel_private_keys(client, conn, channel);
1724 silc_hash_table_free(channel->user_list);
1725 silc_free(channel->channel_name);
1726 silc_free(channel->topic);
1727 if (channel->founder_key)
1728 silc_pkcs_public_key_free(channel->founder_key);
1729 if (channel->internal.send_key)
1730 silc_cipher_free(channel->internal.send_key);
1731 if (channel->internal.receive_key)
1732 silc_cipher_free(channel->internal.receive_key);
1733 if (channel->internal.hmac)
1734 silc_hmac_free(channel->internal.hmac);
1735 if (channel->internal.old_channel_keys) {
1736 silc_dlist_start(channel->internal.old_channel_keys);
1737 while ((key = silc_dlist_get(channel->internal.old_channel_keys)))
1738 silc_cipher_free(key);
1739 silc_dlist_uninit(channel->internal.old_channel_keys);
1741 if (channel->internal.old_hmacs) {
1742 silc_dlist_start(channel->internal.old_hmacs);
1743 while ((hmac = silc_dlist_get(channel->internal.old_hmacs)))
1744 silc_hmac_free(hmac);
1745 silc_dlist_uninit(channel->internal.old_hmacs);
1747 if (channel->channel_pubkeys)
1748 silc_argument_list_free(channel->channel_pubkeys,
1749 SILC_ARGUMENT_PUBLIC_KEY);
1750 silc_atomic_uninit16(&channel->internal.refcnt);
1751 silc_rwlock_free(channel->internal.lock);
1752 silc_schedule_task_del_by_context(conn->client->schedule, channel);
1758 /* Replaces the channel ID of the `channel' to `new_id'. Returns FALSE
1759 if the ID could not be changed. This handles entry locking internally. */
1761 SilcBool silc_client_replace_channel_id(SilcClient client,
1762 SilcClientConnection conn,
1763 SilcChannelEntry channel,
1764 SilcChannelID *new_id)
1766 SilcBool ret = FALSE;
1771 SILC_LOG_DEBUG(("Old Channel ID id(%s)",
1772 silc_id_render(&channel->id, SILC_ID_CHANNEL)));
1773 SILC_LOG_DEBUG(("New Channel ID id(%s)",
1774 silc_id_render(new_id, SILC_ID_CHANNEL)));
1777 silc_rwlock_wrlock(channel->internal.lock);
1778 silc_mutex_lock(conn->internal->lock);
1779 silc_idcache_update_by_context(conn->internal->channel_cache, channel,
1780 new_id, NULL, FALSE);
1781 silc_mutex_unlock(conn->internal->lock);
1782 silc_rwlock_unlock(channel->internal.lock);
1789 void silc_client_lock_channel(SilcChannelEntry channel_entry)
1791 silc_rwlock_rdlock(channel_entry->internal.lock);
1794 /* Unlock channel */
1796 void silc_client_unlock_channel(SilcChannelEntry channel_entry)
1798 silc_rwlock_unlock(channel_entry->internal.lock);
1801 /* Take reference of channel entry */
1803 SilcChannelEntry silc_client_ref_channel(SilcClient client,
1804 SilcClientConnection conn,
1805 SilcChannelEntry channel_entry)
1807 silc_atomic_add_int16(&channel_entry->internal.refcnt, 1);
1808 SILC_LOG_DEBUG(("Channel %p refcnt %d->%d", channel_entry,
1809 silc_atomic_get_int16(&channel_entry->internal.refcnt) - 1,
1810 silc_atomic_get_int16(&channel_entry->internal.refcnt)));
1811 return channel_entry;
1814 /* Release reference of channel entry */
1816 void silc_client_unref_channel(SilcClient client, SilcClientConnection conn,
1817 SilcChannelEntry channel_entry)
1819 if (channel_entry) {
1820 SILC_LOG_DEBUG(("Channel %p refcnt %d->%d", channel_entry,
1821 silc_atomic_get_int16(&channel_entry->internal.refcnt),
1822 silc_atomic_get_int16(&channel_entry->internal.refcnt)
1824 silc_client_del_channel(client, conn, channel_entry);
1828 /* Free channel entry list */
1830 void silc_client_list_free_channels(SilcClient client,
1831 SilcClientConnection conn,
1832 SilcDList channel_list)
1834 SilcChannelEntry channel_entry;
1837 silc_dlist_start(channel_list);
1838 while ((channel_entry = silc_dlist_get(channel_list)))
1839 silc_client_unref_channel(client, conn, channel_entry);
1841 silc_dlist_uninit(channel_list);
1845 /************************* Server Searching Locally *************************/
1847 /* Finds entry for server by the server name. */
1849 SilcServerEntry silc_client_get_server(SilcClient client,
1850 SilcClientConnection conn,
1853 SilcIDCacheEntry id_cache;
1854 SilcServerEntry entry;
1856 if (!client || !conn || !server_name)
1859 SILC_LOG_DEBUG(("Find server by name %s", server_name));
1861 /* Normalize server name for search */
1862 server_name = silc_identifier_check(server_name, strlen(server_name),
1863 SILC_STRING_UTF8, 256, NULL);
1867 silc_mutex_lock(conn->internal->lock);
1869 if (!silc_idcache_find_by_name_one(conn->internal->server_cache,
1870 server_name, &id_cache)) {
1871 silc_free(server_name);
1872 silc_mutex_unlock(conn->internal->lock);
1876 SILC_LOG_DEBUG(("Found"));
1879 entry = id_cache->context;
1880 silc_client_ref_server(client, conn, entry);
1882 silc_mutex_unlock(conn->internal->lock);
1884 silc_free(server_name);
1889 /* Finds entry for server by the server ID. */
1891 SilcServerEntry silc_client_get_server_by_id(SilcClient client,
1892 SilcClientConnection conn,
1893 SilcServerID *server_id)
1895 SilcIDCacheEntry id_cache;
1896 SilcServerEntry entry;
1898 if (!client || !conn || !server_id)
1901 SILC_LOG_DEBUG(("Find server by id %s",
1902 silc_id_render(server_id, SILC_ID_SERVER)));
1904 silc_mutex_lock(conn->internal->lock);
1906 if (!silc_idcache_find_by_id_one(conn->internal->server_cache,
1907 server_id, &id_cache)) {
1908 silc_mutex_unlock(conn->internal->lock);
1912 SILC_LOG_DEBUG(("Found"));
1915 entry = id_cache->context;
1916 silc_client_ref_server(client, conn, entry);
1918 silc_mutex_unlock(conn->internal->lock);
1923 /*********************** Server Resolving from Server ***********************/
1925 /* Resolving context */
1928 SilcGetServerCallback completion;
1930 } *SilcClientGetServerInternal;
1932 /* Resolving command callback */
1934 static SilcBool silc_client_get_server_cb(SilcClient client,
1935 SilcClientConnection conn,
1936 SilcCommand command,
1942 SilcClientGetServerInternal i = context;
1943 SilcServerEntry server;
1945 if (error != SILC_STATUS_OK) {
1946 SILC_LOG_DEBUG(("Resolving failed: %s", silc_get_status_message(error)));
1948 i->completion(client, conn, error, NULL, i->context);
1952 /* Add the returned servers to list */
1953 if (i->completion) {
1954 server = va_arg(ap, SilcServerEntry);
1955 silc_client_ref_server(client, conn, server);
1956 silc_dlist_add(i->servers, server);
1957 server->internal.resolve_cmd_ident = 0;
1960 if (status == SILC_STATUS_OK || status == SILC_STATUS_LIST_END) {
1961 /* Deliver the servers to the caller */
1962 if (i->completion) {
1963 SILC_LOG_DEBUG(("Resolved %d servers", silc_dlist_count(i->servers)));
1964 silc_dlist_start(i->servers);
1965 i->completion(client, conn, SILC_STATUS_OK, i->servers, i->context);
1973 silc_client_list_free_servers(client, conn, i->servers);
1978 /* Resolve server by server ID */
1981 silc_client_get_server_by_id_resolve(SilcClient client,
1982 SilcClientConnection conn,
1983 SilcServerID *server_id,
1984 SilcGetServerCallback completion,
1987 SilcClientGetServerInternal i;
1988 SilcServerEntry server;
1990 SilcUInt16 cmd_ident;
1992 if (!client || !conn || !server_id || !completion)
1995 SILC_LOG_DEBUG(("Resolve server by id %s",
1996 silc_id_render(server_id, SILC_ID_SERVER)));
1998 i = silc_calloc(1, sizeof(*i));
2001 i->completion = completion;
2002 i->context = context;
2003 i->servers = silc_dlist_init();
2009 /* Attach to resolving, if on going */
2010 server = silc_client_get_server_by_id(client, conn, server_id);
2011 if (server && server->internal.resolve_cmd_ident) {
2012 SILC_LOG_DEBUG(("Attach to existing resolving"));
2013 silc_client_unref_server(client, conn, server);
2014 silc_client_command_pending(conn, SILC_COMMAND_NONE,
2015 server->internal.resolve_cmd_ident,
2016 silc_client_get_server_cb, i);
2017 return server->internal.resolve_cmd_ident;
2020 /* Send the command */
2021 idp = silc_id_payload_encode(server_id, SILC_ID_SERVER);
2022 cmd_ident = silc_client_command_send(client, conn, SILC_COMMAND_IDENTIFY,
2023 silc_client_get_server_cb, i, 1,
2024 5, silc_buffer_datalen(idp));
2025 silc_buffer_free(idp);
2026 if (!cmd_ident && completion)
2027 completion(client, conn, SILC_STATUS_ERR_RESOURCE_LIMIT, NULL, context);
2029 if (server && cmd_ident)
2030 server->internal.resolve_cmd_ident = cmd_ident;
2032 silc_client_unref_server(client, conn, server);
2037 /************************** Server Entry Routines ***************************/
2039 /* Add new server entry */
2041 SilcServerEntry silc_client_add_server(SilcClient client,
2042 SilcClientConnection conn,
2043 const char *server_name,
2044 const char *server_info,
2045 SilcServerID *server_id)
2047 SilcServerEntry server_entry;
2048 char *server_namec = NULL;
2053 SILC_LOG_DEBUG(("Adding new server %s", server_name));
2055 server_entry = silc_calloc(1, sizeof(*server_entry));
2059 silc_rwlock_alloc(&server_entry->internal.lock);
2060 silc_atomic_init8(&server_entry->internal.refcnt, 0);
2061 server_entry->id = *server_id;
2063 server_entry->server_name = strdup(server_name);
2065 server_entry->server_info = strdup(server_info);
2067 /* Normalize server name */
2069 server_namec = silc_identifier_check(server_name, strlen(server_name),
2070 SILC_STRING_UTF8, 256, NULL);
2071 if (!server_namec) {
2072 silc_free(server_entry->server_name);
2073 silc_free(server_entry->server_info);
2074 silc_free(server_entry);
2079 silc_mutex_lock(conn->internal->lock);
2081 /* Add server to cache */
2082 if (!silc_idcache_add(conn->internal->server_cache, server_namec,
2083 &server_entry->id, server_entry)) {
2084 silc_free(server_namec);
2085 silc_free(server_entry->server_name);
2086 silc_free(server_entry->server_info);
2087 silc_free(server_entry);
2088 silc_mutex_unlock(conn->internal->lock);
2092 silc_mutex_unlock(conn->internal->lock);
2093 silc_client_ref_server(client, conn, server_entry);
2095 SILC_LOG_DEBUG(("Added %p", server_entry));
2097 return server_entry;
2100 /* Removes server from the cache by the server entry. */
2102 SilcBool silc_client_del_server(SilcClient client, SilcClientConnection conn,
2103 SilcServerEntry server)
2105 SilcIDCacheEntry id_cache;
2106 SilcBool ret = TRUE;
2112 if (silc_atomic_sub_int8(&server->internal.refcnt, 1) > 0)
2115 SILC_LOG_DEBUG(("Deleting server %p", server));
2117 silc_mutex_lock(conn->internal->lock);
2118 if (silc_idcache_find_by_context(conn->internal->server_cache, server,
2120 namec = id_cache->name;
2121 ret = silc_idcache_del_by_context(conn->internal->server_cache,
2125 silc_mutex_unlock(conn->internal->lock);
2127 silc_free(server->server_name);
2128 silc_free(server->server_info);
2129 if (server->public_key)
2130 silc_pkcs_public_key_free(server->public_key);
2131 silc_atomic_uninit8(&server->internal.refcnt);
2132 silc_rwlock_free(server->internal.lock);
2138 /* Updates the `server_entry' with the new information sent as argument. */
2140 void silc_client_update_server(SilcClient client,
2141 SilcClientConnection conn,
2142 SilcServerEntry server_entry,
2143 const char *server_name,
2144 const char *server_info)
2146 char *server_namec = NULL;
2148 SILC_LOG_DEBUG(("Updating server %p", server_entry));
2151 (!server_entry->server_name ||
2152 !silc_utf8_strcasecmp(server_entry->server_name, server_name))) {
2154 server_namec = silc_identifier_check(server_name, strlen(server_name),
2155 SILC_STRING_UTF8, 256, NULL);
2159 silc_free(server_entry->server_name);
2160 server_entry->server_name = strdup(server_name);
2161 if (!server_entry->server_name)
2164 /* Update cache entry */
2165 silc_mutex_lock(conn->internal->lock);
2166 silc_idcache_update_by_context(conn->internal->server_cache, server_entry,
2167 NULL, server_namec, TRUE);
2168 silc_mutex_unlock(conn->internal->lock);
2172 (!server_entry->server_info ||
2173 !silc_utf8_strcasecmp(server_entry->server_info, server_info))) {
2174 silc_free(server_entry->server_info);
2175 server_entry->server_info = strdup(server_info);
2181 void silc_client_lock_server(SilcServerEntry server_entry)
2183 silc_rwlock_rdlock(server_entry->internal.lock);
2188 void silc_client_unlock_server(SilcServerEntry server_entry)
2190 silc_rwlock_unlock(server_entry->internal.lock);
2193 /* Take reference of server entry */
2195 SilcServerEntry silc_client_ref_server(SilcClient client,
2196 SilcClientConnection conn,
2197 SilcServerEntry server_entry)
2199 silc_atomic_add_int8(&server_entry->internal.refcnt, 1);
2200 SILC_LOG_DEBUG(("Server %p refcnt %d->%d", server_entry,
2201 silc_atomic_get_int8(&server_entry->internal.refcnt) - 1,
2202 silc_atomic_get_int8(&server_entry->internal.refcnt)));
2203 return server_entry;
2206 /* Release reference of server entry */
2208 void silc_client_unref_server(SilcClient client, SilcClientConnection conn,
2209 SilcServerEntry server_entry)
2212 SILC_LOG_DEBUG(("Server %p refcnt %d->%d", server_entry,
2213 silc_atomic_get_int8(&server_entry->internal.refcnt),
2214 silc_atomic_get_int8(&server_entry->internal.refcnt)
2216 silc_client_del_server(client, conn, server_entry);
2220 /* Free server entry list */
2222 void silc_client_list_free_servers(SilcClient client,
2223 SilcClientConnection conn,
2224 SilcDList server_list)
2226 SilcServerEntry server_entry;
2229 silc_dlist_start(server_list);
2230 while ((server_entry = silc_dlist_get(server_list)))
2231 silc_client_unref_server(client, conn, server_entry);
2233 silc_dlist_uninit(server_list);