5 Author: Pekka Riikonen <priikone@silcnet.org>
7 Copyright (C) 2001 - 2014 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;
133 if (!get_valid || entry->internal.valid) {
134 silc_client_ref_client(client, conn, id_cache->context);
135 silc_dlist_add(clients, id_cache->context);
139 /* Check multiple cache entries for exact match */
140 while ((id_cache = silc_list_get(list))) {
141 entry = id_cache->context;
145 /* If server was provided, find entries that either have no server
146 set or have the same server. Ignore those that have different
148 if (server[0] && entry->server &&
149 !silc_utf8_strcasecmp(entry->server, server))
152 if (silc_utf8_strcasecmp(entry->nickname,
153 format ? format : parsed) &&
154 (!get_valid || entry->internal.valid)) {
155 silc_client_ref_client(client, conn, entry);
156 silc_dlist_add(clients, entry);
158 /* If format is NULL, we find one exact match with the base
159 nickname (parsed). */
166 silc_mutex_unlock(conn->internal->lock);
168 silc_free(nicknamec);
171 if (!silc_dlist_count(clients)) {
172 silc_dlist_uninit(clients);
176 SILC_LOG_DEBUG(("Found %d clients", silc_dlist_count(clients)));
178 silc_dlist_start(clients);
182 /* Finds clients by nickname from local cache. */
184 SilcDList silc_client_get_clients_local(SilcClient client,
185 SilcClientConnection conn,
186 const char *nickname,
189 return silc_client_get_clients_local_ext(client, conn, nickname, return_all,
193 /********************** Client Resolving from Server ************************/
195 /* Resolving context */
198 SilcGetClientCallback completion;
200 SilcClientEntry client_entry;
201 } *SilcClientGetClientInternal;
203 /* Resolving command callback */
205 static SilcBool silc_client_get_clients_cb(SilcClient client,
206 SilcClientConnection conn,
213 SilcClientGetClientInternal i = context;
214 SilcClientEntry client_entry;
216 if (error != SILC_STATUS_OK) {
217 SILC_LOG_DEBUG(("Resolving failed: %s", silc_get_status_message(error)));
219 if (i->client_entry) {
220 i->client_entry->internal.resolve_cmd_ident = 0;
221 silc_client_unref_client(client, conn, i->client_entry);
225 i->completion(client, conn, error, NULL, i->context);
229 /* Add the returned client to list */
231 client_entry = va_arg(ap, SilcClientEntry);
232 silc_client_ref_client(client, conn, client_entry);
233 silc_dlist_add(i->clients, client_entry);
234 client_entry->internal.resolve_cmd_ident = 0;
237 if (status == SILC_STATUS_OK || status == SILC_STATUS_LIST_END) {
238 /* Deliver the clients to the caller */
240 SILC_LOG_DEBUG(("Resolved %d clients", silc_dlist_count(i->clients)));
242 if (i->client_entry) {
243 i->client_entry->internal.resolve_cmd_ident = 0;
244 silc_client_unref_client(client, conn, i->client_entry);
247 silc_dlist_start(i->clients);
248 i->completion(client, conn, SILC_STATUS_OK, i->clients, i->context);
256 silc_client_list_free(client, conn, i->clients);
261 /* Resolves client information from server by the client ID. */
264 silc_client_get_client_by_id_resolve(SilcClient client,
265 SilcClientConnection conn,
266 SilcClientID *client_id,
267 SilcBuffer attributes,
268 SilcGetClientCallback completion,
271 SilcClientGetClientInternal i;
272 SilcClientEntry client_entry;
274 SilcUInt16 cmd_ident;
276 if (!client || !conn | !client_id) {
277 SILC_LOG_ERROR(("Missing arguments to "
278 "silc_client_get_clients_by_id_resolve call"));
282 SILC_LOG_DEBUG(("Resolve client by ID (%s)",
283 silc_id_render(client_id, SILC_ID_CLIENT)));
285 i = silc_calloc(1, sizeof(*i));
288 i->completion = completion;
289 i->context = context;
290 i->clients = silc_dlist_init();
296 /* Attach to resolving, if on going */
297 client_entry = silc_client_get_client_by_id(client, conn, client_id);
298 if (client_entry && client_entry->internal.resolve_cmd_ident) {
299 SILC_LOG_DEBUG(("Attach to existing resolving"));
300 silc_client_unref_client(client, conn, client_entry);
301 silc_client_command_pending(conn, SILC_COMMAND_NONE,
302 client_entry->internal.resolve_cmd_ident,
303 silc_client_get_clients_cb, i);
304 return client_entry->internal.resolve_cmd_ident;
307 /* Send the command */
308 idp = silc_id_payload_encode(client_id, SILC_ID_CLIENT);
309 cmd_ident = silc_client_command_send(client, conn, SILC_COMMAND_WHOIS,
310 silc_client_get_clients_cb, i,
311 2, 3, silc_buffer_datalen(attributes),
312 4, silc_buffer_datalen(idp));
313 if (!cmd_ident && completion)
314 completion(client, conn, SILC_STATUS_ERR_RESOURCE_LIMIT, NULL, context);
316 if (client_entry && cmd_ident) {
317 client_entry->internal.resolve_cmd_ident = cmd_ident;
318 i->client_entry = client_entry;
320 silc_client_unref_client(client, conn, client_entry);
323 silc_buffer_free(idp);
328 /* Finds client entry or entries by the `nickname' and `server'. The
329 completion callback will be called when the client entries has been
330 found. Used internally by the library. */
332 static SilcUInt16 silc_client_get_clients_i(SilcClient client,
333 SilcClientConnection conn,
335 const char *nickname,
337 SilcBuffer attributes,
338 SilcGetClientCallback completion,
341 SilcClientGetClientInternal i;
342 char nick[128 + 1], serv[256 + 1], userhost[768 + 1], *parsed = NULL;
345 SILC_LOG_DEBUG(("Resolve client by %s command",
346 silc_get_command_name(command)));
348 if (!client || !conn) {
349 SILC_LOG_ERROR(("Missing arguments to silc_client_get_clients call"));
352 if (!nickname && !attributes) {
353 SILC_LOG_ERROR(("Missing arguments to silc_client_get_clients call"));
357 /* Parse server name from the nickname if set */
358 if (silc_parse_userfqdn(nickname, nick, sizeof(nick),
359 serv, sizeof(serv)) == 2)
360 server = (const char *)serv;
361 nickname = (const char *)nick;
363 /* Parse nickname in case it is formatted */
364 if (silc_client_nickname_parse(client, conn, (char *)nickname, &parsed))
365 nickname = (const char *)parsed;
367 i = silc_calloc(1, sizeof(*i));
372 i->clients = silc_dlist_init();
378 i->completion = completion;
379 i->context = context;
381 memset(userhost, 0, sizeof(userhost));
382 if (nickname && server) {
383 len = strlen(nickname) + strlen(server) + 3;
384 silc_strncat(userhost, len, nickname, strlen(nickname));
385 silc_strncat(userhost, len, "@", 1);
386 silc_strncat(userhost, len, server, strlen(server));
387 } else if (nickname) {
388 silc_strncat(userhost, sizeof(userhost) - 1, nickname, strlen(nickname));
392 /* Send the command */
393 if (command == SILC_COMMAND_IDENTIFY)
394 return silc_client_command_send(client, conn, SILC_COMMAND_IDENTIFY,
395 silc_client_get_clients_cb, i,
396 1, 1, userhost, strlen(userhost));
397 return silc_client_command_send(client, conn, SILC_COMMAND_WHOIS,
398 silc_client_get_clients_cb, i,
399 2, 1, userhost, strlen(userhost),
400 3, silc_buffer_datalen(attributes));
403 /* Get clients from server with IDENTIFY command */
405 SilcUInt16 silc_client_get_clients(SilcClient client,
406 SilcClientConnection conn,
407 const char *nickname,
409 SilcGetClientCallback completion,
412 return silc_client_get_clients_i(client, conn, SILC_COMMAND_IDENTIFY,
413 nickname, server, NULL,
414 completion, context);
417 /* Get clients from server with WHOIS command */
419 SilcUInt16 silc_client_get_clients_whois(SilcClient client,
420 SilcClientConnection conn,
421 const char *nickname,
423 SilcBuffer attributes,
424 SilcGetClientCallback completion,
427 return silc_client_get_clients_i(client, conn, SILC_COMMAND_WHOIS,
428 nickname, server, attributes,
429 completion, context);
432 /* ID list resolving context */
434 SilcGetClientCallback completion;
436 SilcBuffer client_id_list;
437 SilcUInt32 list_count;
438 } *GetClientsByListInternal;
440 static SilcBool silc_client_get_clients_list_cb(SilcClient client,
441 SilcClientConnection conn,
448 GetClientsByListInternal i = context;
449 SilcClientEntry client_entry;
455 /* Process the list after all replies have been received */
456 if (status != SILC_STATUS_OK && !SILC_STATUS_IS_ERROR(status) &&
457 status != SILC_STATUS_LIST_END)
460 SILC_LOG_DEBUG(("Resolved all clients"));
462 clients = silc_dlist_init();
464 status = SILC_STATUS_ERR_RESOURCE_LIMIT;
468 for (c = 0; c < i->list_count; c++) {
470 SILC_GET16_MSB(idp_len, i->client_id_list->data + 2);
472 if (!silc_id_payload_parse_id(i->client_id_list->data, idp_len, &id)) {
473 status = SILC_STATUS_ERR_BAD_CLIENT_ID;
477 /* Get client entry */
478 client_entry = silc_client_get_client_by_id(client, conn, &id.u.client_id);
480 silc_dlist_add(clients, client_entry);
482 if (!silc_buffer_pull(i->client_id_list, idp_len)) {
483 status = SILC_STATUS_ERR_BAD_CLIENT_ID;
488 silc_dlist_start(clients);
489 status = SILC_STATUS_OK;
491 i->completion(client, conn, status, clients, i->context);
494 if (status != SILC_STATUS_OK && i->completion)
495 i->completion(client, conn, status, NULL, i->context);
497 silc_client_list_free(client, conn, clients);
498 silc_buffer_free(i->client_id_list);
504 /* Gets client entries by the list of client ID's `client_id_list'. This
505 always resolves those client ID's it does not know yet from the server
506 so this function might take a while. The `client_id_list' is a list
507 of ID Payloads added one after other. JOIN command reply and USERS
508 command reply for example returns this sort of list. The `completion'
509 will be called after the entries are available. */
511 SilcUInt16 silc_client_get_clients_by_list(SilcClient client,
512 SilcClientConnection conn,
513 SilcUInt32 list_count,
514 SilcBuffer client_id_list,
515 SilcGetClientCallback completion,
518 GetClientsByListInternal in;
519 SilcClientEntry entry;
520 unsigned char **res_argv = NULL;
521 SilcUInt32 *res_argv_lens = NULL, *res_argv_types = NULL, res_argc = 0;
522 SilcUInt16 idp_len, cmd_ident;
527 SILC_LOG_DEBUG(("Resolve clients from Client ID list"));
529 if (!client || !conn || !client_id_list)
532 in = silc_calloc(1, sizeof(*in));
535 in->completion = completion;
536 in->context = context;
537 in->list_count = list_count;
538 in->client_id_list = silc_buffer_copy(client_id_list);
539 if (!in->client_id_list)
542 for (i = 0; i < list_count; i++) {
544 SILC_GET16_MSB(idp_len, client_id_list->data + 2);
546 if (!silc_id_payload_parse_id(client_id_list->data, idp_len, &id))
549 /* Check if we have this client cached already. If we don't have the
550 entry or it has incomplete info, then resolve it from the server. */
551 entry = silc_client_get_client_by_id(client, conn, &id.u.client_id);
552 if (!entry || !entry->nickname[0] || !entry->username[0] ||
555 res_argv = silc_calloc(list_count, sizeof(*res_argv));
556 res_argv_lens = silc_calloc(list_count, sizeof(*res_argv_lens));
557 res_argv_types = silc_calloc(list_count, sizeof(*res_argv_types));
558 if (!res_argv || !res_argv_lens || !res_argv_types) {
559 silc_client_unref_client(client, conn, entry);
564 res_argv[res_argc] = client_id_list->data;
565 res_argv_lens[res_argc] = idp_len;
566 res_argv_types[res_argc] = res_argc + 4;
569 silc_client_unref_client(client, conn, entry);
571 if (!silc_buffer_pull(client_id_list, idp_len))
574 silc_buffer_start(client_id_list);
576 /* Query the unknown client information from server */
578 cmd_ident = silc_client_command_send_argv(client,
579 conn, SILC_COMMAND_WHOIS,
580 silc_client_get_clients_list_cb,
581 in, res_argc, res_argv,
585 silc_free(res_argv_lens);
586 silc_free(res_argv_types);
590 /* We have the clients in cache, get them and call the completion */
591 silc_client_get_clients_list_cb(client, conn, SILC_COMMAND_WHOIS,
592 SILC_STATUS_OK, SILC_STATUS_OK, in, tmp);
596 silc_buffer_free(in->client_id_list);
599 silc_free(res_argv_lens);
600 silc_free(res_argv_types);
607 SilcClientConnection conn;
608 SilcChannelID channel_id;
609 SilcGetClientCallback completion;
612 } *GetClientsByChannelInternal;
614 SILC_CLIENT_CMD_FUNC(get_clients_by_channel_cb)
616 GetClientsByChannelInternal i = context;
617 SilcClientEntry *clients = NULL;
618 SilcUInt32 clients_count = 0;
619 SilcBool found = FALSE;
620 SilcChannelEntry channel;
621 SilcHashTableList htl;
630 channel = silc_client_get_channel_by_id(i->client, i->conn, &i->channel_id);
631 if (channel && !silc_hash_table_count(channel->user_list)) {
632 clients = silc_calloc(silc_hash_table_count(channel->user_list),
634 silc_hash_table_list(channel->user_list, &htl);
635 while (silc_hash_table_get(&htl, NULL, (void *)&chu))
636 clients[clients_count++] = chu->client;
637 silc_hash_table_list_reset(&htl);
642 i->completion(i->client, i->conn, clients, clients_count, i->context);
645 i->completion(i->client, i->conn, NULL, 0, i->context);
651 /* Gets client entries by the channel entry indicated by `channel'. Thus,
652 it resolves the clients currently on that channel. */
654 void silc_client_get_clients_by_channel(SilcClient client,
655 SilcClientConnection conn,
656 SilcChannelEntry channel,
657 SilcGetClientCallback completion,
660 GetClientsByChannelInternal in;
661 SilcHashTableList htl;
663 SilcClientEntry entry;
664 unsigned char **res_argv = NULL;
665 SilcUInt32 *res_argv_lens = NULL, *res_argv_types = NULL, res_argc = 0;
667 SilcBool wait_res = FALSE;
669 assert(client && conn && channel);
671 SILC_LOG_DEBUG(("Start"));
673 in = silc_calloc(1, sizeof(*in));
676 in->channel_id = *channel->id;
677 in->completion = completion;
678 in->context = context;
680 /* If user list does not exist, send USERS command. */
681 if (!channel->user_list || !silc_hash_table_count(channel->user_list)) {
682 SILC_LOG_DEBUG(("Sending USERS"));
683 silc_client_command_register(client, SILC_COMMAND_USERS, NULL, NULL,
684 silc_client_command_reply_users_i, 0,
686 silc_client_command_send(client, conn, SILC_COMMAND_USERS,
687 conn->cmd_ident, 1, 2, channel->channel_name,
688 strlen(channel->channel_name));
689 silc_client_command_pending(conn, SILC_COMMAND_USERS, conn->cmd_ident,
690 silc_client_command_get_clients_by_channel_cb,
695 silc_hash_table_list(channel->user_list, &htl);
696 while (silc_hash_table_get(&htl, NULL, (void *)&chu)) {
699 /* If the entry has incomplete info, then resolve it from the server. */
700 if (!entry->nickname[0] || !entry->realname) {
701 if (entry->status & SILC_CLIENT_STATUS_RESOLVING) {
702 /* Attach to this resolving and wait until it finishes */
703 silc_client_command_pending(
704 conn, SILC_COMMAND_NONE,
705 entry->resolve_cmd_ident,
706 silc_client_command_get_clients_by_channel_cb,
712 entry->status |= SILC_CLIENT_STATUS_RESOLVING;
713 entry->resolve_cmd_ident = conn->cmd_ident + 1;
715 idp = silc_id_payload_encode(entry->id, SILC_ID_CLIENT);
717 /* No we don't have it, query it from the server. Assemble argument
718 table that will be sent for the WHOIS command later. */
719 res_argv = silc_realloc(res_argv, sizeof(*res_argv) *
721 res_argv_lens = silc_realloc(res_argv_lens, sizeof(*res_argv_lens) *
723 res_argv_types = silc_realloc(res_argv_types, sizeof(*res_argv_types) *
725 res_argv[res_argc] = silc_memdup(idp->data, idp->len);
726 res_argv_lens[res_argc] = idp->len;
727 res_argv_types[res_argc] = res_argc + 4;
730 silc_buffer_free(idp);
733 silc_hash_table_list_reset(&htl);
735 /* Query the client information from server if the list included clients
736 that we don't know about. */
740 /* Send the WHOIS command to server */
741 res_cmd = silc_command_payload_encode(SILC_COMMAND_WHOIS,
742 res_argc, res_argv, res_argv_lens,
743 res_argv_types, ++conn->cmd_ident);
744 silc_client_packet_send(client, conn->sock, SILC_PACKET_COMMAND,
745 NULL, 0, NULL, NULL, res_cmd->data, res_cmd->len,
748 /* Register our own command reply for this command */
749 silc_client_command_register(client, SILC_COMMAND_WHOIS, NULL, NULL,
750 silc_client_command_reply_whois_i, 0,
753 /* Process the applications request after reply has been received */
754 silc_client_command_pending(
755 conn, SILC_COMMAND_WHOIS, conn->cmd_ident,
756 silc_client_command_get_clients_by_channel_cb,
760 silc_buffer_free(res_cmd);
762 silc_free(res_argv_lens);
763 silc_free(res_argv_types);
770 /* We have the clients in cache, get them and call the completion */
771 silc_client_command_get_clients_by_channel_cb((void *)in, NULL);
776 /************************** Client Entry Routines ***************************/
778 /* Creates new client entry and adds it to the ID cache. Returns pointer
781 SilcClientEntry silc_client_add_client(SilcClient client,
782 SilcClientConnection conn,
783 char *nickname, char *username,
784 char *userinfo, SilcClientID *id,
787 SilcClientEntry client_entry;
788 char *nick = NULL, parsed[128 + 1];
790 SILC_LOG_DEBUG(("Adding new client entry"));
792 /* Save the client infos */
793 client_entry = silc_calloc(1, sizeof(*client_entry));
797 silc_rwlock_alloc(&client_entry->internal.lock);
798 silc_atomic_init32(&client_entry->internal.refcnt, 0);
799 silc_atomic_init32(&client_entry->internal.deleted, 1);
800 client_entry->id = *id;
801 client_entry->mode = mode;
802 client_entry->realname = userinfo ? strdup(userinfo) : NULL;
804 silc_parse_userfqdn(nickname, parsed, sizeof(parsed),
805 client_entry->server, sizeof(client_entry->server));
806 if (nickname && client->internal->params->full_nicknames)
807 silc_snprintf(client_entry->nickname, sizeof(client_entry->nickname),
810 silc_snprintf(client_entry->nickname, sizeof(client_entry->nickname),
813 silc_parse_userfqdn(username, client_entry->username,
814 sizeof(client_entry->username),
815 client_entry->hostname,
816 sizeof(client_entry->hostname));
818 client_entry->channels = silc_hash_table_alloc(1, silc_hash_ptr, NULL, NULL,
819 NULL, NULL, NULL, TRUE);
820 if (!client_entry->channels) {
821 silc_free(client_entry->realname);
822 silc_atomic_uninit32(&client_entry->internal.deleted);
823 silc_atomic_uninit32(&client_entry->internal.refcnt);
824 silc_rwlock_free(client_entry->internal.lock);
825 silc_free(client_entry);
829 /* Normalize nickname */
830 if (client_entry->nickname[0]) {
831 nick = silc_identifier_check(parsed, strlen(parsed),
832 SILC_STRING_UTF8, 128, NULL);
834 silc_hash_table_free(client_entry->channels);
835 silc_free(client_entry->realname);
836 silc_atomic_uninit32(&client_entry->internal.deleted);
837 silc_atomic_uninit32(&client_entry->internal.refcnt);
838 silc_rwlock_free(client_entry->internal.lock);
839 silc_free(client_entry);
844 silc_mutex_lock(conn->internal->lock);
846 /* Add client to cache, the normalized nickname is saved to cache */
847 if (!silc_idcache_add(conn->internal->client_cache, nick,
848 &client_entry->id, client_entry)) {
850 silc_hash_table_free(client_entry->channels);
851 silc_free(client_entry->realname);
852 silc_atomic_uninit32(&client_entry->internal.deleted);
853 silc_atomic_uninit32(&client_entry->internal.refcnt);
854 silc_rwlock_free(client_entry->internal.lock);
855 silc_free(client_entry);
856 silc_mutex_unlock(conn->internal->lock);
860 client_entry->nickname_normalized = nick;
862 silc_mutex_unlock(conn->internal->lock);
863 silc_client_ref_client(client, conn, client_entry);
865 /* Format the nickname */
866 silc_client_nickname_format(client, conn, client_entry, FALSE);
868 if (client_entry->nickname[0])
869 client_entry->internal.valid = TRUE;
871 SILC_LOG_DEBUG(("Added %p", client_entry));
876 /* Updates the `client_entry' with the new information sent as argument.
877 This handles entry locking internally. */
879 void silc_client_update_client(SilcClient client,
880 SilcClientConnection conn,
881 SilcClientEntry client_entry,
882 const char *nickname,
883 const char *username,
884 const char *userinfo,
887 char *nick = NULL, parsed[128 + 1];
889 SILC_LOG_DEBUG(("Update client entry"));
891 silc_rwlock_wrlock(client_entry->internal.lock);
893 if (!client_entry->realname && userinfo)
894 client_entry->realname = strdup(userinfo);
896 if ((!client_entry->username[0] || !client_entry->hostname[0]) && username)
897 silc_parse_userfqdn(username, client_entry->username,
898 sizeof(client_entry->username),
899 client_entry->hostname,
900 sizeof(client_entry->username));
902 if (!client_entry->nickname[0] && nickname) {
903 silc_parse_userfqdn(nickname, parsed, sizeof(parsed),
904 client_entry->server, sizeof(client_entry->server));
905 if (client->internal->params->full_nicknames)
906 silc_snprintf(client_entry->nickname, sizeof(client_entry->nickname),
909 silc_snprintf(client_entry->nickname, sizeof(client_entry->nickname),
912 /* Normalize nickname */
913 nick = silc_identifier_check(parsed, strlen(parsed),
914 SILC_STRING_UTF8, 128, NULL);
916 silc_rwlock_unlock(client_entry->internal.lock);
920 /* Format nickname */
921 silc_client_nickname_format(client, conn, client_entry,
922 client_entry == conn->local_entry);
924 /* Update cache entry */
925 silc_mutex_lock(conn->internal->lock);
926 silc_idcache_update_by_context(conn->internal->client_cache,
927 client_entry, NULL, nick, TRUE);
928 silc_mutex_unlock(conn->internal->lock);
929 client_entry->nickname_normalized = nick;
930 client_entry->internal.valid = TRUE;
932 client_entry->mode = mode;
934 silc_rwlock_unlock(client_entry->internal.lock);
937 /* Change a client's nickname. Must be called with `client_entry' locked. */
939 SilcBool silc_client_change_nickname(SilcClient client,
940 SilcClientConnection conn,
941 SilcClientEntry client_entry,
942 const char *new_nick,
943 SilcClientID *new_id,
944 const unsigned char *idp,
949 SILC_LOG_DEBUG(("Change nickname %s to %s", client_entry->nickname,
952 /* Normalize nickname */
953 tmp = silc_identifier_check(new_nick, strlen(new_nick),
954 SILC_STRING_UTF8, 128, NULL);
958 /* Update the client entry */
959 silc_mutex_lock(conn->internal->lock);
960 if (!silc_idcache_update_by_context(conn->internal->client_cache,
961 client_entry, new_id, tmp, TRUE)) {
963 silc_mutex_unlock(conn->internal->lock);
966 silc_mutex_unlock(conn->internal->lock);
968 memset(client_entry->nickname, 0, sizeof(client_entry->nickname));
969 memcpy(client_entry->nickname, new_nick, strlen(new_nick));
970 client_entry->nickname_normalized = tmp;
971 silc_client_nickname_format(client, conn, client_entry,
972 client_entry == conn->local_entry);
974 /* For my client entry, update ID and set new ID to packet stream */
975 if (client_entry == conn->local_entry) {
976 if (idp && idp_len) {
977 silc_buffer_enlarge(conn->internal->local_idp, idp_len);
978 silc_buffer_put(conn->internal->local_idp, idp, idp_len);
981 silc_packet_set_ids(conn->stream, SILC_ID_CLIENT, conn->local_id,
985 client_entry->internal.valid = TRUE;
989 /* Deletes the client entry and frees all memory. */
991 void silc_client_del_client_entry(SilcClient client,
992 SilcClientConnection conn,
993 SilcClientEntry client_entry)
995 silc_free(client_entry->realname);
996 silc_free(client_entry->nickname_normalized);
997 silc_free(client_entry->internal.key);
998 if (client_entry->public_key)
999 silc_pkcs_public_key_free(client_entry->public_key);
1000 silc_hash_table_free(client_entry->channels);
1001 if (client_entry->internal.send_key)
1002 silc_cipher_free(client_entry->internal.send_key);
1003 if (client_entry->internal.receive_key)
1004 silc_cipher_free(client_entry->internal.receive_key);
1005 if (client_entry->internal.hmac_send)
1006 silc_hmac_free(client_entry->internal.hmac_send);
1007 if (client_entry->internal.hmac_receive)
1008 silc_hmac_free(client_entry->internal.hmac_receive);
1009 silc_client_ftp_session_free_client(client, client_entry);
1010 if (client_entry->internal.op)
1011 silc_async_abort(client_entry->internal.op, NULL, NULL);
1012 client_entry->internal.op = NULL;
1013 if (client_entry->internal.ke)
1014 silc_client_abort_key_agreement(client, conn, client_entry);
1015 silc_atomic_uninit32(&client_entry->internal.deleted);
1016 silc_atomic_uninit32(&client_entry->internal.refcnt);
1017 silc_rwlock_free(client_entry->internal.lock);
1018 silc_free(client_entry);
1021 /* Removes client from the cache by the client entry. */
1023 SilcBool silc_client_del_client(SilcClient client, SilcClientConnection conn,
1024 SilcClientEntry client_entry)
1029 SILC_LOG_DEBUG(("Marking client entry %p deleted", client_entry));
1031 if (silc_atomic_sub_int32(&client_entry->internal.deleted, 1) != 0) {
1032 SILC_LOG_DEBUG(("Client entry %p already marked deleted", client_entry));
1036 /* Abort ongoing operation */
1037 if (client_entry->internal.op) {
1038 SILC_LOG_DEBUG(("Aborting ongoing operation %p",
1039 client_entry->internal.op));
1040 silc_async_abort(client_entry->internal.op, NULL, NULL);
1041 client_entry->internal.op = NULL;
1044 silc_client_unref_client(client, conn, client_entry);
1048 /* Internal routine used to find client by ID and if not found this creates
1049 new client entry and returns it. */
1051 SilcClientEntry silc_client_get_client(SilcClient client,
1052 SilcClientConnection conn,
1053 SilcClientID *client_id)
1055 SilcClientEntry client_entry;
1057 client_entry = silc_client_get_client_by_id(client, conn, client_id);
1058 if (!client_entry) {
1059 client_entry = silc_client_add_client(client, conn, NULL, NULL, NULL,
1063 silc_client_ref_client(client, conn, client_entry);
1066 return client_entry;
1071 void silc_client_lock_client(SilcClientEntry client_entry)
1073 silc_rwlock_rdlock(client_entry->internal.lock);
1078 void silc_client_unlock_client(SilcClientEntry client_entry)
1080 silc_rwlock_unlock(client_entry->internal.lock);
1083 /* Take reference of client entry */
1085 SilcClientEntry silc_client_ref_client(SilcClient client,
1086 SilcClientConnection conn,
1087 SilcClientEntry client_entry)
1089 silc_atomic_add_int32(&client_entry->internal.refcnt, 1);
1090 SILC_LOG_DEBUG(("Client %p refcnt %d->%d", client_entry,
1091 silc_atomic_get_int32(&client_entry->internal.refcnt) - 1,
1092 silc_atomic_get_int32(&client_entry->internal.refcnt)));
1093 return client_entry;
1096 /* Release reference of client entry */
1098 void silc_client_unref_client(SilcClient client, SilcClientConnection conn,
1099 SilcClientEntry client_entry)
1106 SILC_LOG_DEBUG(("Client %p refcnt %d->%d", client_entry,
1107 silc_atomic_get_int32(&client_entry->internal.refcnt),
1108 silc_atomic_get_int32(&client_entry->internal.refcnt) - 1));
1110 if (silc_atomic_sub_int32(&client_entry->internal.refcnt, 1) > 0)
1113 SILC_LOG_DEBUG(("Deleting client %p (%d)", client_entry,
1114 silc_atomic_get_int32(&client_entry->internal.deleted)));
1116 silc_mutex_lock(conn->internal->lock);
1117 ret = silc_idcache_del_by_context(conn->internal->client_cache,
1118 client_entry, NULL);
1119 silc_mutex_unlock(conn->internal->lock);
1122 /* Remove from channels */
1123 silc_client_remove_from_channels(client, conn, client_entry);
1125 /* Free the client entry data */
1126 silc_client_del_client_entry(client, conn, client_entry);
1130 /* Free client entry list */
1132 void silc_client_list_free(SilcClient client, SilcClientConnection conn,
1133 SilcDList client_list)
1135 SilcClientEntry client_entry;
1138 silc_dlist_start(client_list);
1139 while ((client_entry = silc_dlist_get(client_list)))
1140 silc_client_unref_client(client, conn, client_entry);
1142 silc_dlist_uninit(client_list);
1146 /* Formats the nickname of the client specified by the `client_entry'.
1147 If the format is specified by the application this will format the
1148 nickname and replace the old nickname in the client entry. If the
1149 format string is not specified then this function has no effect.
1150 Returns the client entry that was formatted. */
1152 SilcClientEntry silc_client_nickname_format(SilcClient client,
1153 SilcClientConnection conn,
1154 SilcClientEntry client_entry,
1158 char newnick[128 + 1];
1159 int i, off = 0, len;
1161 SilcClientEntry entry, unformatted = NULL;
1162 SilcBool formatted = FALSE;
1164 if (!client->internal->params->nickname_format[0])
1165 return client_entry;
1166 if (!client_entry->nickname[0])
1169 SILC_LOG_DEBUG(("Format nickname"));
1171 /* Get all clients with same nickname. Do not perform the formatting
1172 if there aren't any clients with same nickname unless the application
1173 is forcing us to do so. */
1174 clients = silc_client_get_clients_local_ext(client, conn,
1175 client_entry->nickname,
1179 if (silc_dlist_count(clients) == 1 && !priority &&
1180 !client->internal->params->nickname_force_format) {
1181 silc_client_list_free(client, conn, clients);
1182 return client_entry;
1185 /* Is the requested client formatted already */
1186 if (client_entry->nickname_normalized &&
1187 !silc_utf8_strcasecmp(client_entry->nickname,
1188 client_entry->nickname_normalized))
1191 if (client->internal->params->nickname_force_format)
1194 /* Find unformatted client entry */
1195 while ((entry = silc_dlist_get(clients))) {
1196 if (!entry->internal.valid)
1198 if (entry == client_entry)
1200 if (silc_utf8_strcasecmp(entry->nickname, entry->nickname_normalized)) {
1201 unformatted = entry;
1206 /* If there are no other unformatted clients and the requested client is
1207 unformatted, just return it. */
1208 if (!unformatted && !formatted) {
1209 silc_client_list_free(client, conn, clients);
1210 return client_entry;
1213 /* If priority formatting then the requested client will get the
1214 unformatted nickname, and the unformatted client will get a new
1215 formatted nickname. */
1218 /* Simply change the client's nickname to unformatted */
1219 if (!silc_client_nickname_parse(client, conn, client_entry->nickname,
1223 silc_snprintf(client_entry->nickname, sizeof(client_entry->nickname),
1229 /* There was no other unformatted client */
1230 silc_client_list_free(client, conn, clients);
1231 return client_entry;
1234 /* Now format the previously unformatted client */
1235 client_entry = unformatted;
1239 /* If already formatted just return it */
1241 silc_client_list_free(client, conn, clients);
1242 return client_entry;
1245 memset(newnick, 0, sizeof(newnick));
1246 cp = client->internal->params->nickname_format;
1256 if (!client_entry->nickname[0])
1258 len = strlen(client_entry->nickname);
1259 memcpy(&newnick[off], client_entry->nickname, len);
1263 /* Stripped hostname */
1264 if (!client_entry->hostname[0])
1266 len = strcspn(client_entry->hostname, ".");
1267 i = strcspn(client_entry->hostname, "-");
1270 memcpy(&newnick[off], client_entry->hostname, len);
1275 if (!client_entry->hostname[0])
1277 len = strlen(client_entry->hostname);
1278 memcpy(&newnick[off], client_entry->hostname, len);
1282 /* Ascending number */
1287 if (silc_dlist_count(clients) == 1)
1290 silc_dlist_start(clients);
1291 while ((entry = silc_dlist_get(clients))) {
1292 if (!silc_utf8_strncasecmp(entry->nickname, newnick, off))
1294 if (strlen(entry->nickname) <= off)
1296 num = atoi(&entry->nickname[off]);
1301 memset(tmp, 0, sizeof(tmp));
1302 silc_snprintf(tmp, sizeof(tmp) - 1, "%d", ++max);
1304 memcpy(&newnick[off], tmp, len);
1309 /* Some other character in the string */
1310 memcpy(&newnick[off], cp, 1);
1319 memset(client_entry->nickname, 0, sizeof(client_entry->nickname));
1320 memcpy(client_entry->nickname, newnick, strlen(newnick));
1321 silc_client_list_free(client, conn, clients);
1323 return client_entry;
1326 /* Parses nickname according to nickname format string */
1328 SilcBool silc_client_nickname_parse(SilcClient client,
1329 SilcClientConnection conn,
1333 char *cp, s = 0, e = 0, *nick;
1337 if (!client->internal->params->nickname_format[0]) {
1342 if (!nickname || !nickname[0])
1345 cp = client->internal->params->nickname_format;
1363 /* Get separator character */
1376 /* Parse the nickname */
1380 if (strchr(nickname, s))
1381 nick = strchr(nickname, s) + 1;
1383 if (strchr(nick, e))
1384 len = strchr(nick, e) - nick;
1388 *ret_nick = silc_memdup(nick, len);
1392 SILC_LOG_DEBUG(("Parsed nickname: %s", *ret_nick));
1397 /************************ Channel Searching Locally *************************/
1399 /* Finds entry for channel by the channel name. Returns the entry or NULL
1400 if the entry was not found. It is found only if the client is joined
1403 SilcChannelEntry silc_client_get_channel(SilcClient client,
1404 SilcClientConnection conn,
1408 SilcIDCacheEntry id_cache;
1409 SilcChannelEntry entry = NULL;
1410 char chname[256 + 1], server[256 + 1];
1412 if (!client || !conn || !channel)
1415 SILC_LOG_DEBUG(("Find channel %s", channel));
1417 /* Parse server name from channel name */
1418 silc_parse_userfqdn(channel, chname, sizeof(chname), server, sizeof(server));
1420 /* Normalize name for search */
1421 channel = silc_channel_name_check(chname, strlen(chname), SILC_STRING_UTF8,
1426 silc_mutex_lock(conn->internal->lock);
1428 if (!silc_idcache_find_by_name(conn->internal->channel_cache, channel,
1430 silc_mutex_unlock(conn->internal->lock);
1435 /* If server name was specified with channel name, find the correct
1436 channel entry with the server name. There can only be one channel
1437 with same name on same server. */
1438 silc_list_start(list);
1440 while ((id_cache = silc_list_get(list))) {
1441 entry = id_cache->context;
1442 if (!entry->server[0])
1444 if (silc_utf8_strcasecmp(entry->server, server))
1448 /* Get first channel without server name specified or one with our
1449 current server connection name */
1450 while ((id_cache = silc_list_get(list))) {
1451 entry = id_cache->context;
1452 if (!entry->server[0])
1454 if (silc_utf8_strcasecmp(entry->server, conn->remote_host))
1460 silc_mutex_unlock(conn->internal->lock);
1465 SILC_LOG_DEBUG(("Found channel %s%s%s", entry->channel_name,
1466 entry->server[0] ? "@" : "", entry->server));
1469 silc_client_ref_channel(client, conn, entry);
1470 silc_mutex_unlock(conn->internal->lock);
1477 /* Finds entry for channel by the channel ID. Returns the entry or NULL
1478 if the entry was not found. It is found only if the client is joined
1481 SilcChannelEntry silc_client_get_channel_by_id(SilcClient client,
1482 SilcClientConnection conn,
1483 SilcChannelID *channel_id)
1485 SilcIDCacheEntry id_cache;
1486 SilcChannelEntry entry;
1488 if (!client || !conn || !channel_id)
1491 SILC_LOG_DEBUG(("Find channel by id %s",
1492 silc_id_render(channel_id, SILC_ID_CHANNEL)));
1494 silc_mutex_lock(conn->internal->lock);
1496 if (!silc_idcache_find_by_id_one(conn->internal->channel_cache, channel_id,
1498 silc_mutex_unlock(conn->internal->lock);
1502 SILC_LOG_DEBUG(("Found"));
1504 entry = id_cache->context;
1507 silc_client_ref_channel(client, conn, entry);
1508 silc_mutex_unlock(conn->internal->lock);
1513 /********************** Channel Resolving from Server ***********************/
1515 /* Channel resolving context */
1518 SilcGetChannelCallback completion;
1520 } *SilcClientGetChannelInternal;
1522 /* Resolving command callback */
1524 static SilcBool silc_client_get_channel_cb(SilcClient client,
1525 SilcClientConnection conn,
1526 SilcCommand command,
1532 SilcClientGetChannelInternal i = context;
1533 SilcChannelEntry entry;
1535 if (error != SILC_STATUS_OK) {
1536 SILC_LOG_DEBUG(("Resolving failed: %s", silc_get_status_message(error)));
1538 i->completion(client, conn, error, NULL, i->context);
1542 /* Add the returned channel to list */
1543 if (i->completion) {
1544 entry = va_arg(ap, SilcChannelEntry);
1545 silc_client_ref_channel(client, conn, entry);
1546 silc_dlist_add(i->channels, entry);
1549 if (status == SILC_STATUS_OK || status == SILC_STATUS_LIST_END) {
1550 /* Deliver the channels to the caller */
1551 if (i->completion) {
1552 SILC_LOG_DEBUG(("Resolved %d channels", silc_dlist_count(i->channels)));
1553 silc_dlist_start(i->channels);
1554 i->completion(client, conn, SILC_STATUS_OK, i->channels, i->context);
1562 silc_client_list_free_channels(client, conn, i->channels);
1567 /* Resolves channel entry from the server by the channel name. */
1569 void silc_client_get_channel_resolve(SilcClient client,
1570 SilcClientConnection conn,
1572 SilcGetChannelCallback completion,
1575 SilcClientGetChannelInternal i;
1577 if (!client || !conn || !channel_name || !completion)
1580 SILC_LOG_DEBUG(("Resolve channel %s", channel_name));
1582 i = silc_calloc(1, sizeof(*i));
1585 i->completion = completion;
1586 i->context = context;
1587 i->channels = silc_dlist_init();
1593 /* Send the command */
1594 if (!silc_client_command_send(client, conn, SILC_COMMAND_IDENTIFY,
1595 silc_client_get_channel_cb, i, 1,
1596 3, channel_name, strlen(channel_name))) {
1598 completion(client, conn, SILC_STATUS_ERR_RESOURCE_LIMIT, NULL, context);
1602 /* Resolves channel information from the server by the channel ID. */
1605 silc_client_get_channel_by_id_resolve(SilcClient client,
1606 SilcClientConnection conn,
1607 SilcChannelID *channel_id,
1608 SilcGetChannelCallback completion,
1611 SilcClientGetChannelInternal i;
1613 SilcUInt16 cmd_ident;
1615 if (!client || !conn || !channel_id || !completion)
1618 SILC_LOG_DEBUG(("Resolve channel by id %s",
1619 silc_id_render(channel_id, SILC_ID_CHANNEL)));
1621 i = silc_calloc(1, sizeof(*i));
1624 i->completion = completion;
1625 i->context = context;
1626 i->channels = silc_dlist_init();
1632 /* Send the command */
1633 idp = silc_id_payload_encode(channel_id, SILC_ID_CHANNEL);
1634 cmd_ident = silc_client_command_send(client, conn, SILC_COMMAND_IDENTIFY,
1635 silc_client_get_channel_cb, i, 1,
1636 5, silc_buffer_datalen(idp));
1637 silc_buffer_free(idp);
1638 if (!cmd_ident && completion)
1639 completion(client, conn, SILC_STATUS_ERR_RESOURCE_LIMIT, NULL, context);
1644 /************************* Channel Entry Routines ***************************/
1646 /* Add new channel entry to the ID Cache */
1648 SilcChannelEntry silc_client_add_channel(SilcClient client,
1649 SilcClientConnection conn,
1650 const char *channel_name,
1652 SilcChannelID *channel_id)
1654 SilcChannelEntry channel;
1655 char *channel_namec, name[256 + 1];
1657 SILC_LOG_DEBUG(("Adding channel %s", channel_name));
1659 channel = silc_calloc(1, sizeof(*channel));
1663 silc_rwlock_alloc(&channel->internal.lock);
1664 silc_atomic_init32(&channel->internal.refcnt, 0);
1665 silc_atomic_init32(&channel->internal.deleted, 1);
1666 channel->id = *channel_id;
1667 channel->mode = mode;
1669 silc_parse_userfqdn(channel_name, name, sizeof(name),
1670 channel->server, sizeof(channel->server));
1671 if (client->internal->params->full_channel_names)
1672 channel->channel_name = strdup(channel_name);
1674 channel->channel_name = strdup(name);
1676 if (!channel->channel_name) {
1677 silc_rwlock_free(channel->internal.lock);
1678 silc_atomic_uninit32(&channel->internal.refcnt);
1679 silc_atomic_uninit32(&channel->internal.deleted);
1684 channel->user_list = silc_hash_table_alloc(1, silc_hash_ptr, NULL, NULL,
1685 NULL, NULL, NULL, TRUE);
1686 if (!channel->user_list) {
1687 silc_rwlock_free(channel->internal.lock);
1688 silc_atomic_uninit32(&channel->internal.refcnt);
1689 silc_atomic_uninit32(&channel->internal.deleted);
1690 silc_free(channel->channel_name);
1695 /* Normalize channel name */
1696 channel_namec = silc_channel_name_check(name, strlen(name),
1697 SILC_STRING_UTF8, 256, NULL);
1698 if (!channel_namec) {
1699 silc_rwlock_free(channel->internal.lock);
1700 silc_atomic_uninit32(&channel->internal.refcnt);
1701 silc_atomic_uninit32(&channel->internal.deleted);
1702 silc_free(channel->channel_name);
1703 silc_hash_table_free(channel->user_list);
1708 silc_mutex_lock(conn->internal->lock);
1710 /* Add channel to cache, the normalized channel name is saved to cache */
1711 if (!silc_idcache_add(conn->internal->channel_cache, channel_namec,
1712 &channel->id, channel)) {
1713 silc_rwlock_free(channel->internal.lock);
1714 silc_atomic_uninit32(&channel->internal.refcnt);
1715 silc_atomic_uninit32(&channel->internal.deleted);
1716 silc_free(channel_namec);
1717 silc_free(channel->channel_name);
1718 silc_hash_table_free(channel->user_list);
1720 silc_mutex_unlock(conn->internal->lock);
1724 silc_mutex_unlock(conn->internal->lock);
1725 silc_client_ref_channel(client, conn, channel);
1727 SILC_LOG_DEBUG(("Added %p", channel));
1732 /* Removes channel from the cache by the channel entry. */
1734 SilcBool silc_client_del_channel(SilcClient client, SilcClientConnection conn,
1735 SilcChannelEntry channel)
1740 SILC_LOG_DEBUG(("Marking channel entry %p deleted", channel));
1742 if (silc_atomic_sub_int32(&channel->internal.deleted, 1) != 0) {
1743 SILC_LOG_DEBUG(("Channel entry %p already marked deleted", channel));
1747 silc_client_unref_channel(client, conn, channel);
1751 /* Replaces the channel ID of the `channel' to `new_id'. Returns FALSE
1752 if the ID could not be changed. This handles entry locking internally. */
1754 SilcBool silc_client_replace_channel_id(SilcClient client,
1755 SilcClientConnection conn,
1756 SilcChannelEntry channel,
1757 SilcChannelID *new_id)
1759 SilcBool ret = FALSE;
1764 SILC_LOG_DEBUG(("Old Channel ID id(%s)",
1765 silc_id_render(&channel->id, SILC_ID_CHANNEL)));
1766 SILC_LOG_DEBUG(("New Channel ID id(%s)",
1767 silc_id_render(new_id, SILC_ID_CHANNEL)));
1770 silc_rwlock_wrlock(channel->internal.lock);
1771 silc_mutex_lock(conn->internal->lock);
1772 silc_idcache_update_by_context(conn->internal->channel_cache, channel,
1773 new_id, NULL, FALSE);
1774 silc_mutex_unlock(conn->internal->lock);
1775 silc_rwlock_unlock(channel->internal.lock);
1782 void silc_client_lock_channel(SilcChannelEntry channel_entry)
1784 silc_rwlock_rdlock(channel_entry->internal.lock);
1787 /* Unlock channel */
1789 void silc_client_unlock_channel(SilcChannelEntry channel_entry)
1791 silc_rwlock_unlock(channel_entry->internal.lock);
1794 /* Take reference of channel entry */
1796 SilcChannelEntry silc_client_ref_channel(SilcClient client,
1797 SilcClientConnection conn,
1798 SilcChannelEntry channel_entry)
1800 silc_atomic_add_int32(&channel_entry->internal.refcnt, 1);
1801 SILC_LOG_DEBUG(("Channel %p refcnt %d->%d", channel_entry,
1802 silc_atomic_get_int32(&channel_entry->internal.refcnt) - 1,
1803 silc_atomic_get_int32(&channel_entry->internal.refcnt)));
1804 return channel_entry;
1807 /* Release reference of channel entry */
1809 void silc_client_unref_channel(SilcClient client, SilcClientConnection conn,
1810 SilcChannelEntry channel_entry)
1812 SilcIDCacheEntry id_cache;
1813 SilcBool ret = TRUE;
1821 SILC_LOG_DEBUG(("Channel %p refcnt %d->%d", channel_entry,
1822 silc_atomic_get_int32(&channel_entry->internal.refcnt),
1823 silc_atomic_get_int32(&channel_entry->internal.refcnt)
1826 if (silc_atomic_sub_int32(&channel_entry->internal.refcnt, 1) > 0)
1829 SILC_LOG_DEBUG(("Deleting channel %p", channel_entry));
1831 silc_mutex_lock(conn->internal->lock);
1832 if (silc_idcache_find_by_context(conn->internal->channel_cache, channel_entry,
1834 namec = id_cache->name;
1835 ret = silc_idcache_del_by_context(conn->internal->channel_cache,
1836 channel_entry, NULL);
1839 silc_mutex_unlock(conn->internal->lock);
1844 silc_client_empty_channel(client, conn, channel_entry);
1845 silc_client_del_channel_private_keys(client, conn, channel_entry);
1846 silc_hash_table_free(channel_entry->user_list);
1847 silc_free(channel_entry->channel_name);
1848 silc_free(channel_entry->topic);
1849 if (channel_entry->founder_key)
1850 silc_pkcs_public_key_free(channel_entry->founder_key);
1851 if (channel_entry->internal.send_key)
1852 silc_cipher_free(channel_entry->internal.send_key);
1853 if (channel_entry->internal.receive_key)
1854 silc_cipher_free(channel_entry->internal.receive_key);
1855 if (channel_entry->internal.hmac)
1856 silc_hmac_free(channel_entry->internal.hmac);
1857 if (channel_entry->internal.old_channel_keys) {
1858 silc_dlist_start(channel_entry->internal.old_channel_keys);
1859 while ((key = silc_dlist_get(channel_entry->internal.old_channel_keys)))
1860 silc_cipher_free(key);
1861 silc_dlist_uninit(channel_entry->internal.old_channel_keys);
1863 if (channel_entry->internal.old_hmacs) {
1864 silc_dlist_start(channel_entry->internal.old_hmacs);
1865 while ((hmac = silc_dlist_get(channel_entry->internal.old_hmacs)))
1866 silc_hmac_free(hmac);
1867 silc_dlist_uninit(channel_entry->internal.old_hmacs);
1869 if (channel_entry->channel_pubkeys)
1870 silc_argument_list_free(channel_entry->channel_pubkeys,
1871 SILC_ARGUMENT_PUBLIC_KEY);
1872 silc_atomic_uninit32(&channel_entry->internal.deleted);
1873 silc_atomic_uninit32(&channel_entry->internal.refcnt);
1874 silc_rwlock_free(channel_entry->internal.lock);
1875 silc_schedule_task_del_by_context(conn->client->schedule, channel_entry);
1876 silc_free(channel_entry);
1879 /* Free channel entry list */
1881 void silc_client_list_free_channels(SilcClient client,
1882 SilcClientConnection conn,
1883 SilcDList channel_list)
1885 SilcChannelEntry channel_entry;
1888 silc_dlist_start(channel_list);
1889 while ((channel_entry = silc_dlist_get(channel_list)))
1890 silc_client_unref_channel(client, conn, channel_entry);
1892 silc_dlist_uninit(channel_list);
1896 /************************* Server Searching Locally *************************/
1898 /* Finds entry for server by the server name. */
1900 SilcServerEntry silc_client_get_server(SilcClient client,
1901 SilcClientConnection conn,
1904 SilcIDCacheEntry id_cache;
1905 SilcServerEntry entry;
1907 if (!client || !conn || !server_name)
1910 SILC_LOG_DEBUG(("Find server by name %s", server_name));
1912 /* Normalize server name for search */
1913 server_name = silc_identifier_check(server_name, strlen(server_name),
1914 SILC_STRING_UTF8, 256, NULL);
1918 silc_mutex_lock(conn->internal->lock);
1920 if (!silc_idcache_find_by_name_one(conn->internal->server_cache,
1921 server_name, &id_cache)) {
1922 silc_free(server_name);
1923 silc_mutex_unlock(conn->internal->lock);
1927 SILC_LOG_DEBUG(("Found"));
1930 entry = id_cache->context;
1931 silc_client_ref_server(client, conn, entry);
1933 silc_mutex_unlock(conn->internal->lock);
1935 silc_free(server_name);
1940 /* Finds entry for server by the server ID. */
1942 SilcServerEntry silc_client_get_server_by_id(SilcClient client,
1943 SilcClientConnection conn,
1944 SilcServerID *server_id)
1946 SilcIDCacheEntry id_cache;
1947 SilcServerEntry entry;
1949 if (!client || !conn || !server_id)
1952 SILC_LOG_DEBUG(("Find server by id %s",
1953 silc_id_render(server_id, SILC_ID_SERVER)));
1955 silc_mutex_lock(conn->internal->lock);
1957 if (!silc_idcache_find_by_id_one(conn->internal->server_cache,
1958 server_id, &id_cache)) {
1959 silc_mutex_unlock(conn->internal->lock);
1963 SILC_LOG_DEBUG(("Found"));
1966 entry = id_cache->context;
1967 silc_client_ref_server(client, conn, entry);
1969 silc_mutex_unlock(conn->internal->lock);
1974 /*********************** Server Resolving from Server ***********************/
1976 /* Resolving context */
1979 SilcGetServerCallback completion;
1981 } *SilcClientGetServerInternal;
1983 /* Resolving command callback */
1985 static SilcBool silc_client_get_server_cb(SilcClient client,
1986 SilcClientConnection conn,
1987 SilcCommand command,
1993 SilcClientGetServerInternal i = context;
1994 SilcServerEntry server;
1996 if (error != SILC_STATUS_OK) {
1997 SILC_LOG_DEBUG(("Resolving failed: %s", silc_get_status_message(error)));
1999 i->completion(client, conn, error, NULL, i->context);
2003 /* Add the returned servers to list */
2004 if (i->completion) {
2005 server = va_arg(ap, SilcServerEntry);
2006 silc_client_ref_server(client, conn, server);
2007 silc_dlist_add(i->servers, server);
2008 server->internal.resolve_cmd_ident = 0;
2011 if (status == SILC_STATUS_OK || status == SILC_STATUS_LIST_END) {
2012 /* Deliver the servers to the caller */
2013 if (i->completion) {
2014 SILC_LOG_DEBUG(("Resolved %d servers", silc_dlist_count(i->servers)));
2015 silc_dlist_start(i->servers);
2016 i->completion(client, conn, SILC_STATUS_OK, i->servers, i->context);
2024 silc_client_list_free_servers(client, conn, i->servers);
2029 /* Resolve server by server ID */
2032 silc_client_get_server_by_id_resolve(SilcClient client,
2033 SilcClientConnection conn,
2034 SilcServerID *server_id,
2035 SilcGetServerCallback completion,
2038 SilcClientGetServerInternal i;
2039 SilcServerEntry server;
2041 SilcUInt16 cmd_ident;
2043 if (!client || !conn || !server_id || !completion)
2046 SILC_LOG_DEBUG(("Resolve server by id %s",
2047 silc_id_render(server_id, SILC_ID_SERVER)));
2049 i = silc_calloc(1, sizeof(*i));
2052 i->completion = completion;
2053 i->context = context;
2054 i->servers = silc_dlist_init();
2060 /* Attach to resolving, if on going */
2061 server = silc_client_get_server_by_id(client, conn, server_id);
2062 if (server && server->internal.resolve_cmd_ident) {
2063 SILC_LOG_DEBUG(("Attach to existing resolving"));
2064 silc_client_unref_server(client, conn, server);
2065 silc_client_command_pending(conn, SILC_COMMAND_NONE,
2066 server->internal.resolve_cmd_ident,
2067 silc_client_get_server_cb, i);
2068 return server->internal.resolve_cmd_ident;
2071 /* Send the command */
2072 idp = silc_id_payload_encode(server_id, SILC_ID_SERVER);
2073 cmd_ident = silc_client_command_send(client, conn, SILC_COMMAND_IDENTIFY,
2074 silc_client_get_server_cb, i, 1,
2075 5, silc_buffer_datalen(idp));
2076 silc_buffer_free(idp);
2077 if (!cmd_ident && completion)
2078 completion(client, conn, SILC_STATUS_ERR_RESOURCE_LIMIT, NULL, context);
2080 if (server && cmd_ident)
2081 server->internal.resolve_cmd_ident = cmd_ident;
2083 silc_client_unref_server(client, conn, server);
2088 /************************** Server Entry Routines ***************************/
2090 /* Add new server entry */
2092 SilcServerEntry silc_client_add_server(SilcClient client,
2093 SilcClientConnection conn,
2094 const char *server_name,
2095 const char *server_info,
2096 SilcServerID *server_id)
2098 SilcServerEntry server_entry;
2099 char *server_namec = NULL;
2104 SILC_LOG_DEBUG(("Adding new server %s", server_name));
2106 server_entry = silc_calloc(1, sizeof(*server_entry));
2110 silc_rwlock_alloc(&server_entry->internal.lock);
2111 silc_atomic_init32(&server_entry->internal.refcnt, 0);
2112 silc_atomic_init32(&server_entry->internal.deleted, 1);
2113 server_entry->id = *server_id;
2115 server_entry->server_name = strdup(server_name);
2117 server_entry->server_info = strdup(server_info);
2119 /* Normalize server name */
2121 server_namec = silc_identifier_check(server_name, strlen(server_name),
2122 SILC_STRING_UTF8, 256, NULL);
2123 if (!server_namec) {
2124 silc_free(server_entry->server_name);
2125 silc_free(server_entry->server_info);
2126 silc_atomic_uninit32(&server_entry->internal.deleted);
2127 silc_atomic_uninit32(&server_entry->internal.refcnt);
2128 silc_rwlock_free(server_entry->internal.lock);
2129 silc_free(server_entry);
2134 silc_mutex_lock(conn->internal->lock);
2136 /* Add server to cache */
2137 if (!silc_idcache_add(conn->internal->server_cache, server_namec,
2138 &server_entry->id, server_entry)) {
2139 silc_free(server_namec);
2140 silc_free(server_entry->server_name);
2141 silc_free(server_entry->server_info);
2142 silc_atomic_uninit32(&server_entry->internal.deleted);
2143 silc_atomic_uninit32(&server_entry->internal.refcnt);
2144 silc_rwlock_free(server_entry->internal.lock);
2145 silc_free(server_entry);
2146 silc_mutex_unlock(conn->internal->lock);
2150 silc_mutex_unlock(conn->internal->lock);
2151 silc_client_ref_server(client, conn, server_entry);
2153 SILC_LOG_DEBUG(("Added %p", server_entry));
2155 return server_entry;
2158 /* Removes server from the cache by the server entry. */
2160 SilcBool silc_client_del_server(SilcClient client, SilcClientConnection conn,
2161 SilcServerEntry server)
2166 if (silc_atomic_sub_int32(&server->internal.deleted, 1) != 0)
2169 silc_client_unref_server(client, conn, server);
2173 /* Updates the `server_entry' with the new information sent as argument. */
2175 void silc_client_update_server(SilcClient client,
2176 SilcClientConnection conn,
2177 SilcServerEntry server_entry,
2178 const char *server_name,
2179 const char *server_info)
2181 char *server_namec = NULL;
2183 SILC_LOG_DEBUG(("Updating server %p", server_entry));
2186 (!server_entry->server_name ||
2187 !silc_utf8_strcasecmp(server_entry->server_name, server_name))) {
2189 server_namec = silc_identifier_check(server_name, strlen(server_name),
2190 SILC_STRING_UTF8, 256, NULL);
2194 silc_free(server_entry->server_name);
2195 server_entry->server_name = strdup(server_name);
2196 if (!server_entry->server_name)
2199 /* Update cache entry */
2200 silc_mutex_lock(conn->internal->lock);
2201 silc_idcache_update_by_context(conn->internal->server_cache, server_entry,
2202 NULL, server_namec, TRUE);
2203 silc_mutex_unlock(conn->internal->lock);
2207 (!server_entry->server_info ||
2208 !silc_utf8_strcasecmp(server_entry->server_info, server_info))) {
2209 silc_free(server_entry->server_info);
2210 server_entry->server_info = strdup(server_info);
2216 void silc_client_lock_server(SilcServerEntry server_entry)
2218 silc_rwlock_rdlock(server_entry->internal.lock);
2223 void silc_client_unlock_server(SilcServerEntry server_entry)
2225 silc_rwlock_unlock(server_entry->internal.lock);
2228 /* Take reference of server entry */
2230 SilcServerEntry silc_client_ref_server(SilcClient client,
2231 SilcClientConnection conn,
2232 SilcServerEntry server_entry)
2234 silc_atomic_add_int32(&server_entry->internal.refcnt, 1);
2235 SILC_LOG_DEBUG(("Server %p refcnt %d->%d", server_entry,
2236 silc_atomic_get_int32(&server_entry->internal.refcnt) - 1,
2237 silc_atomic_get_int32(&server_entry->internal.refcnt)));
2238 return server_entry;
2241 /* Release reference of server entry */
2243 void silc_client_unref_server(SilcClient client, SilcClientConnection conn,
2244 SilcServerEntry server_entry)
2246 SilcIDCacheEntry id_cache;
2252 if (silc_atomic_sub_int32(&server_entry->internal.refcnt, 1) > 0)
2255 SILC_LOG_DEBUG(("Deleting server %p", server_entry));
2257 silc_mutex_lock(conn->internal->lock);
2258 if (silc_idcache_find_by_context(conn->internal->server_cache, server_entry,
2260 namec = id_cache->name;
2261 silc_idcache_del_by_context(conn->internal->server_cache,
2262 server_entry, NULL);
2265 silc_mutex_unlock(conn->internal->lock);
2267 silc_free(server_entry->server_name);
2268 silc_free(server_entry->server_info);
2269 if (server_entry->public_key)
2270 silc_pkcs_public_key_free(server_entry->public_key);
2271 silc_atomic_uninit32(&server_entry->internal.deleted);
2272 silc_atomic_uninit32(&server_entry->internal.refcnt);
2273 silc_rwlock_free(server_entry->internal.lock);
2274 silc_free(server_entry);
2277 /* Free server entry list */
2279 void silc_client_list_free_servers(SilcClient client,
2280 SilcClientConnection conn,
2281 SilcDList server_list)
2283 SilcServerEntry server_entry;
2286 silc_dlist_start(server_list);
2287 while ((server_entry = silc_dlist_get(server_list)))
2288 silc_client_unref_server(client, conn, server_entry);
2290 silc_dlist_uninit(server_list);