5 Author: Pekka Riikonen <priikone@silcnet.org>
7 Copyright (C) 2001 - 2008 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.ke)
1011 silc_client_abort_key_agreement(client, conn, client_entry);
1012 silc_atomic_uninit32(&client_entry->internal.deleted);
1013 silc_atomic_uninit32(&client_entry->internal.refcnt);
1014 silc_rwlock_free(client_entry->internal.lock);
1015 silc_free(client_entry);
1018 /* Removes client from the cache by the client entry. */
1020 SilcBool silc_client_del_client(SilcClient client, SilcClientConnection conn,
1021 SilcClientEntry client_entry)
1026 SILC_LOG_DEBUG(("Marking client entry %p deleted", client_entry));
1028 if (silc_atomic_sub_int32(&client_entry->internal.deleted, 1) != 0) {
1029 SILC_LOG_DEBUG(("Client entry %p already marked deleted", client_entry));
1033 silc_client_unref_client(client, conn, client_entry);
1037 /* Internal routine used to find client by ID and if not found this creates
1038 new client entry and returns it. */
1040 SilcClientEntry silc_client_get_client(SilcClient client,
1041 SilcClientConnection conn,
1042 SilcClientID *client_id)
1044 SilcClientEntry client_entry;
1046 client_entry = silc_client_get_client_by_id(client, conn, client_id);
1047 if (!client_entry) {
1048 client_entry = silc_client_add_client(client, conn, NULL, NULL, NULL,
1052 silc_client_ref_client(client, conn, client_entry);
1055 return client_entry;
1060 void silc_client_lock_client(SilcClientEntry client_entry)
1062 silc_rwlock_rdlock(client_entry->internal.lock);
1067 void silc_client_unlock_client(SilcClientEntry client_entry)
1069 silc_rwlock_unlock(client_entry->internal.lock);
1072 /* Take reference of client entry */
1074 SilcClientEntry silc_client_ref_client(SilcClient client,
1075 SilcClientConnection conn,
1076 SilcClientEntry client_entry)
1078 silc_atomic_add_int32(&client_entry->internal.refcnt, 1);
1079 SILC_LOG_DEBUG(("Client %p refcnt %d->%d", client_entry,
1080 silc_atomic_get_int32(&client_entry->internal.refcnt) - 1,
1081 silc_atomic_get_int32(&client_entry->internal.refcnt)));
1082 return client_entry;
1085 /* Release reference of client entry */
1087 void silc_client_unref_client(SilcClient client, SilcClientConnection conn,
1088 SilcClientEntry client_entry)
1095 SILC_LOG_DEBUG(("Client %p refcnt %d->%d", client_entry,
1096 silc_atomic_get_int32(&client_entry->internal.refcnt),
1097 silc_atomic_get_int32(&client_entry->internal.refcnt) - 1));
1099 if (silc_atomic_sub_int32(&client_entry->internal.refcnt, 1) > 0)
1102 SILC_LOG_DEBUG(("Deleting client %p (%d)", client_entry,
1103 silc_atomic_get_int32(&client_entry->internal.deleted)));
1105 silc_mutex_lock(conn->internal->lock);
1106 ret = silc_idcache_del_by_context(conn->internal->client_cache,
1107 client_entry, NULL);
1108 silc_mutex_unlock(conn->internal->lock);
1111 /* Remove from channels */
1112 silc_client_remove_from_channels(client, conn, client_entry);
1114 /* Free the client entry data */
1115 silc_client_del_client_entry(client, conn, client_entry);
1119 /* Free client entry list */
1121 void silc_client_list_free(SilcClient client, SilcClientConnection conn,
1122 SilcDList client_list)
1124 SilcClientEntry client_entry;
1127 silc_dlist_start(client_list);
1128 while ((client_entry = silc_dlist_get(client_list)))
1129 silc_client_unref_client(client, conn, client_entry);
1131 silc_dlist_uninit(client_list);
1135 /* Formats the nickname of the client specified by the `client_entry'.
1136 If the format is specified by the application this will format the
1137 nickname and replace the old nickname in the client entry. If the
1138 format string is not specified then this function has no effect.
1139 Returns the client entry that was formatted. */
1141 SilcClientEntry silc_client_nickname_format(SilcClient client,
1142 SilcClientConnection conn,
1143 SilcClientEntry client_entry,
1147 char newnick[128 + 1];
1148 int i, off = 0, len;
1150 SilcClientEntry entry, unformatted = NULL;
1151 SilcBool formatted = FALSE;
1153 if (!client->internal->params->nickname_format[0])
1154 return client_entry;
1155 if (!client_entry->nickname[0])
1158 SILC_LOG_DEBUG(("Format nickname"));
1160 /* Get all clients with same nickname. Do not perform the formatting
1161 if there aren't any clients with same nickname unless the application
1162 is forcing us to do so. */
1163 clients = silc_client_get_clients_local_ext(client, conn,
1164 client_entry->nickname,
1168 if (silc_dlist_count(clients) == 1 && !priority &&
1169 !client->internal->params->nickname_force_format) {
1170 silc_client_list_free(client, conn, clients);
1171 return client_entry;
1174 /* Is the requested client formatted already */
1175 if (client_entry->nickname_normalized &&
1176 !silc_utf8_strcasecmp(client_entry->nickname,
1177 client_entry->nickname_normalized))
1180 if (client->internal->params->nickname_force_format)
1183 /* Find unformatted client entry */
1184 while ((entry = silc_dlist_get(clients))) {
1185 if (!entry->internal.valid)
1187 if (entry == client_entry)
1189 if (silc_utf8_strcasecmp(entry->nickname, entry->nickname_normalized)) {
1190 unformatted = entry;
1195 /* If there are no other unformatted clients and the requested client is
1196 unformatted, just return it. */
1197 if (!unformatted && !formatted) {
1198 silc_client_list_free(client, conn, clients);
1199 return client_entry;
1202 /* If priority formatting then the requested client will get the
1203 unformatted nickname, and the unformatted client will get a new
1204 formatted nickname. */
1207 /* Simply change the client's nickname to unformatted */
1208 if (!silc_client_nickname_parse(client, conn, client_entry->nickname,
1212 silc_snprintf(client_entry->nickname, sizeof(client_entry->nickname),
1218 /* There was no other unformatted client */
1219 silc_client_list_free(client, conn, clients);
1220 return client_entry;
1223 /* Now format the previously unformatted client */
1224 client_entry = unformatted;
1228 /* If already formatted just return it */
1230 silc_client_list_free(client, conn, clients);
1231 return client_entry;
1234 memset(newnick, 0, sizeof(newnick));
1235 cp = client->internal->params->nickname_format;
1245 if (!client_entry->nickname[0])
1247 len = strlen(client_entry->nickname);
1248 memcpy(&newnick[off], client_entry->nickname, len);
1252 /* Stripped hostname */
1253 if (!client_entry->hostname[0])
1255 len = strcspn(client_entry->hostname, ".");
1256 i = strcspn(client_entry->hostname, "-");
1259 memcpy(&newnick[off], client_entry->hostname, len);
1264 if (!client_entry->hostname[0])
1266 len = strlen(client_entry->hostname);
1267 memcpy(&newnick[off], client_entry->hostname, len);
1271 /* Ascending number */
1276 if (silc_dlist_count(clients) == 1)
1279 silc_dlist_start(clients);
1280 while ((entry = silc_dlist_get(clients))) {
1281 if (!silc_utf8_strncasecmp(entry->nickname, newnick, off))
1283 if (strlen(entry->nickname) <= off)
1285 num = atoi(&entry->nickname[off]);
1290 memset(tmp, 0, sizeof(tmp));
1291 silc_snprintf(tmp, sizeof(tmp) - 1, "%d", ++max);
1293 memcpy(&newnick[off], tmp, len);
1298 /* Some other character in the string */
1299 memcpy(&newnick[off], cp, 1);
1308 memset(client_entry->nickname, 0, sizeof(client_entry->nickname));
1309 memcpy(client_entry->nickname, newnick, strlen(newnick));
1310 silc_client_list_free(client, conn, clients);
1312 return client_entry;
1315 /* Parses nickname according to nickname format string */
1317 SilcBool silc_client_nickname_parse(SilcClient client,
1318 SilcClientConnection conn,
1322 char *cp, s = 0, e = 0, *nick;
1326 if (!client->internal->params->nickname_format[0]) {
1331 if (!nickname || !nickname[0])
1334 cp = client->internal->params->nickname_format;
1352 /* Get separator character */
1365 /* Parse the nickname */
1369 if (strchr(nickname, s))
1370 nick = strchr(nickname, s) + 1;
1372 if (strchr(nick, e))
1373 len = strchr(nick, e) - nick;
1377 *ret_nick = silc_memdup(nick, len);
1381 SILC_LOG_DEBUG(("Parsed nickname: %s", *ret_nick));
1386 /************************ Channel Searching Locally *************************/
1388 /* Finds entry for channel by the channel name. Returns the entry or NULL
1389 if the entry was not found. It is found only if the client is joined
1392 SilcChannelEntry silc_client_get_channel(SilcClient client,
1393 SilcClientConnection conn,
1397 SilcIDCacheEntry id_cache;
1398 SilcChannelEntry entry = NULL;
1399 char chname[256 + 1], server[256 + 1];
1401 if (!client || !conn || !channel)
1404 SILC_LOG_DEBUG(("Find channel %s", channel));
1406 /* Parse server name from channel name */
1407 silc_parse_userfqdn(channel, chname, sizeof(chname), server, sizeof(server));
1409 /* Normalize name for search */
1410 channel = silc_channel_name_check(chname, strlen(chname), SILC_STRING_UTF8,
1415 silc_mutex_lock(conn->internal->lock);
1417 if (!silc_idcache_find_by_name(conn->internal->channel_cache, channel,
1419 silc_mutex_unlock(conn->internal->lock);
1424 /* If server name was specified with channel name, find the correct
1425 channel entry with the server name. There can only be one channel
1426 with same name on same server. */
1427 silc_list_start(list);
1429 while ((id_cache = silc_list_get(list))) {
1430 entry = id_cache->context;
1431 if (!entry->server[0])
1433 if (silc_utf8_strcasecmp(entry->server, server))
1437 /* Get first channel without server name specified or one with our
1438 current server connection name */
1439 while ((id_cache = silc_list_get(list))) {
1440 entry = id_cache->context;
1441 if (!entry->server[0])
1443 if (silc_utf8_strcasecmp(entry->server, conn->remote_host))
1449 silc_mutex_unlock(conn->internal->lock);
1454 SILC_LOG_DEBUG(("Found channel %s%s%s", entry->channel_name,
1455 entry->server[0] ? "@" : "", entry->server));
1458 silc_client_ref_channel(client, conn, entry);
1459 silc_mutex_unlock(conn->internal->lock);
1466 /* Finds entry for channel by the channel ID. Returns the entry or NULL
1467 if the entry was not found. It is found only if the client is joined
1470 SilcChannelEntry silc_client_get_channel_by_id(SilcClient client,
1471 SilcClientConnection conn,
1472 SilcChannelID *channel_id)
1474 SilcIDCacheEntry id_cache;
1475 SilcChannelEntry entry;
1477 if (!client || !conn || !channel_id)
1480 SILC_LOG_DEBUG(("Find channel by id %s",
1481 silc_id_render(channel_id, SILC_ID_CHANNEL)));
1483 silc_mutex_lock(conn->internal->lock);
1485 if (!silc_idcache_find_by_id_one(conn->internal->channel_cache, channel_id,
1487 silc_mutex_unlock(conn->internal->lock);
1491 SILC_LOG_DEBUG(("Found"));
1493 entry = id_cache->context;
1496 silc_client_ref_channel(client, conn, entry);
1497 silc_mutex_unlock(conn->internal->lock);
1502 /********************** Channel Resolving from Server ***********************/
1504 /* Channel resolving context */
1507 SilcGetChannelCallback completion;
1509 } *SilcClientGetChannelInternal;
1511 /* Resolving command callback */
1513 static SilcBool silc_client_get_channel_cb(SilcClient client,
1514 SilcClientConnection conn,
1515 SilcCommand command,
1521 SilcClientGetChannelInternal i = context;
1522 SilcChannelEntry entry;
1524 if (error != SILC_STATUS_OK) {
1525 SILC_LOG_DEBUG(("Resolving failed: %s", silc_get_status_message(error)));
1527 i->completion(client, conn, error, NULL, i->context);
1531 /* Add the returned channel to list */
1532 if (i->completion) {
1533 entry = va_arg(ap, SilcChannelEntry);
1534 silc_client_ref_channel(client, conn, entry);
1535 silc_dlist_add(i->channels, entry);
1538 if (status == SILC_STATUS_OK || status == SILC_STATUS_LIST_END) {
1539 /* Deliver the channels to the caller */
1540 if (i->completion) {
1541 SILC_LOG_DEBUG(("Resolved %d channels", silc_dlist_count(i->channels)));
1542 silc_dlist_start(i->channels);
1543 i->completion(client, conn, SILC_STATUS_OK, i->channels, i->context);
1551 silc_client_list_free_channels(client, conn, i->channels);
1556 /* Resolves channel entry from the server by the channel name. */
1558 void silc_client_get_channel_resolve(SilcClient client,
1559 SilcClientConnection conn,
1561 SilcGetChannelCallback completion,
1564 SilcClientGetChannelInternal i;
1566 if (!client || !conn || !channel_name || !completion)
1569 SILC_LOG_DEBUG(("Resolve channel %s", channel_name));
1571 i = silc_calloc(1, sizeof(*i));
1574 i->completion = completion;
1575 i->context = context;
1576 i->channels = silc_dlist_init();
1582 /* Send the command */
1583 if (!silc_client_command_send(client, conn, SILC_COMMAND_IDENTIFY,
1584 silc_client_get_channel_cb, i, 1,
1585 3, channel_name, strlen(channel_name))) {
1587 completion(client, conn, SILC_STATUS_ERR_RESOURCE_LIMIT, NULL, context);
1591 /* Resolves channel information from the server by the channel ID. */
1594 silc_client_get_channel_by_id_resolve(SilcClient client,
1595 SilcClientConnection conn,
1596 SilcChannelID *channel_id,
1597 SilcGetChannelCallback completion,
1600 SilcClientGetChannelInternal i;
1602 SilcUInt16 cmd_ident;
1604 if (!client || !conn || !channel_id || !completion)
1607 SILC_LOG_DEBUG(("Resolve channel by id %s",
1608 silc_id_render(channel_id, SILC_ID_CHANNEL)));
1610 i = silc_calloc(1, sizeof(*i));
1613 i->completion = completion;
1614 i->context = context;
1615 i->channels = silc_dlist_init();
1621 /* Send the command */
1622 idp = silc_id_payload_encode(channel_id, SILC_ID_CHANNEL);
1623 cmd_ident = silc_client_command_send(client, conn, SILC_COMMAND_IDENTIFY,
1624 silc_client_get_channel_cb, i, 1,
1625 5, silc_buffer_datalen(idp));
1626 silc_buffer_free(idp);
1627 if (!cmd_ident && completion)
1628 completion(client, conn, SILC_STATUS_ERR_RESOURCE_LIMIT, NULL, context);
1633 /************************* Channel Entry Routines ***************************/
1635 /* Add new channel entry to the ID Cache */
1637 SilcChannelEntry silc_client_add_channel(SilcClient client,
1638 SilcClientConnection conn,
1639 const char *channel_name,
1641 SilcChannelID *channel_id)
1643 SilcChannelEntry channel;
1644 char *channel_namec, name[256 + 1];
1646 SILC_LOG_DEBUG(("Adding channel %s", channel_name));
1648 channel = silc_calloc(1, sizeof(*channel));
1652 silc_rwlock_alloc(&channel->internal.lock);
1653 silc_atomic_init32(&channel->internal.refcnt, 0);
1654 silc_atomic_init32(&channel->internal.deleted, 1);
1655 channel->id = *channel_id;
1656 channel->mode = mode;
1658 silc_parse_userfqdn(channel_name, name, sizeof(name),
1659 channel->server, sizeof(channel->server));
1660 if (client->internal->params->full_channel_names)
1661 channel->channel_name = strdup(channel_name);
1663 channel->channel_name = strdup(name);
1665 if (!channel->channel_name) {
1666 silc_rwlock_free(channel->internal.lock);
1667 silc_atomic_uninit32(&channel->internal.refcnt);
1668 silc_atomic_uninit32(&channel->internal.deleted);
1673 channel->user_list = silc_hash_table_alloc(1, silc_hash_ptr, NULL, NULL,
1674 NULL, NULL, NULL, TRUE);
1675 if (!channel->user_list) {
1676 silc_rwlock_free(channel->internal.lock);
1677 silc_atomic_uninit32(&channel->internal.refcnt);
1678 silc_atomic_uninit32(&channel->internal.deleted);
1679 silc_free(channel->channel_name);
1684 /* Normalize channel name */
1685 channel_namec = silc_channel_name_check(name, strlen(name),
1686 SILC_STRING_UTF8, 256, NULL);
1687 if (!channel_namec) {
1688 silc_rwlock_free(channel->internal.lock);
1689 silc_atomic_uninit32(&channel->internal.refcnt);
1690 silc_atomic_uninit32(&channel->internal.deleted);
1691 silc_free(channel->channel_name);
1692 silc_hash_table_free(channel->user_list);
1697 silc_mutex_lock(conn->internal->lock);
1699 /* Add channel to cache, the normalized channel name is saved to cache */
1700 if (!silc_idcache_add(conn->internal->channel_cache, channel_namec,
1701 &channel->id, channel)) {
1702 silc_rwlock_free(channel->internal.lock);
1703 silc_atomic_uninit32(&channel->internal.refcnt);
1704 silc_atomic_uninit32(&channel->internal.deleted);
1705 silc_free(channel_namec);
1706 silc_free(channel->channel_name);
1707 silc_hash_table_free(channel->user_list);
1709 silc_mutex_unlock(conn->internal->lock);
1713 silc_mutex_unlock(conn->internal->lock);
1714 silc_client_ref_channel(client, conn, channel);
1716 SILC_LOG_DEBUG(("Added %p", channel));
1721 /* Removes channel from the cache by the channel entry. */
1723 SilcBool silc_client_del_channel(SilcClient client, SilcClientConnection conn,
1724 SilcChannelEntry channel)
1729 SILC_LOG_DEBUG(("Marking channel entry %p deleted", channel));
1731 if (silc_atomic_sub_int32(&channel->internal.deleted, 1) != 0) {
1732 SILC_LOG_DEBUG(("Channel entry %p already marked deleted", channel));
1736 silc_client_unref_channel(client, conn, channel);
1740 /* Replaces the channel ID of the `channel' to `new_id'. Returns FALSE
1741 if the ID could not be changed. This handles entry locking internally. */
1743 SilcBool silc_client_replace_channel_id(SilcClient client,
1744 SilcClientConnection conn,
1745 SilcChannelEntry channel,
1746 SilcChannelID *new_id)
1748 SilcBool ret = FALSE;
1753 SILC_LOG_DEBUG(("Old Channel ID id(%s)",
1754 silc_id_render(&channel->id, SILC_ID_CHANNEL)));
1755 SILC_LOG_DEBUG(("New Channel ID id(%s)",
1756 silc_id_render(new_id, SILC_ID_CHANNEL)));
1759 silc_rwlock_wrlock(channel->internal.lock);
1760 silc_mutex_lock(conn->internal->lock);
1761 silc_idcache_update_by_context(conn->internal->channel_cache, channel,
1762 new_id, NULL, FALSE);
1763 silc_mutex_unlock(conn->internal->lock);
1764 silc_rwlock_unlock(channel->internal.lock);
1771 void silc_client_lock_channel(SilcChannelEntry channel_entry)
1773 silc_rwlock_rdlock(channel_entry->internal.lock);
1776 /* Unlock channel */
1778 void silc_client_unlock_channel(SilcChannelEntry channel_entry)
1780 silc_rwlock_unlock(channel_entry->internal.lock);
1783 /* Take reference of channel entry */
1785 SilcChannelEntry silc_client_ref_channel(SilcClient client,
1786 SilcClientConnection conn,
1787 SilcChannelEntry channel_entry)
1789 silc_atomic_add_int32(&channel_entry->internal.refcnt, 1);
1790 SILC_LOG_DEBUG(("Channel %p refcnt %d->%d", channel_entry,
1791 silc_atomic_get_int32(&channel_entry->internal.refcnt) - 1,
1792 silc_atomic_get_int32(&channel_entry->internal.refcnt)));
1793 return channel_entry;
1796 /* Release reference of channel entry */
1798 void silc_client_unref_channel(SilcClient client, SilcClientConnection conn,
1799 SilcChannelEntry channel_entry)
1801 SilcIDCacheEntry id_cache;
1802 SilcBool ret = TRUE;
1810 SILC_LOG_DEBUG(("Channel %p refcnt %d->%d", channel_entry,
1811 silc_atomic_get_int32(&channel_entry->internal.refcnt),
1812 silc_atomic_get_int32(&channel_entry->internal.refcnt)
1815 if (silc_atomic_sub_int32(&channel_entry->internal.refcnt, 1) > 0)
1818 SILC_LOG_DEBUG(("Deleting channel %p", channel_entry));
1820 silc_mutex_lock(conn->internal->lock);
1821 if (silc_idcache_find_by_context(conn->internal->channel_cache, channel_entry,
1823 namec = id_cache->name;
1824 ret = silc_idcache_del_by_context(conn->internal->channel_cache,
1825 channel_entry, NULL);
1828 silc_mutex_unlock(conn->internal->lock);
1833 silc_client_empty_channel(client, conn, channel_entry);
1834 silc_client_del_channel_private_keys(client, conn, channel_entry);
1835 silc_hash_table_free(channel_entry->user_list);
1836 silc_free(channel_entry->channel_name);
1837 silc_free(channel_entry->topic);
1838 if (channel_entry->founder_key)
1839 silc_pkcs_public_key_free(channel_entry->founder_key);
1840 if (channel_entry->internal.send_key)
1841 silc_cipher_free(channel_entry->internal.send_key);
1842 if (channel_entry->internal.receive_key)
1843 silc_cipher_free(channel_entry->internal.receive_key);
1844 if (channel_entry->internal.hmac)
1845 silc_hmac_free(channel_entry->internal.hmac);
1846 if (channel_entry->internal.old_channel_keys) {
1847 silc_dlist_start(channel_entry->internal.old_channel_keys);
1848 while ((key = silc_dlist_get(channel_entry->internal.old_channel_keys)))
1849 silc_cipher_free(key);
1850 silc_dlist_uninit(channel_entry->internal.old_channel_keys);
1852 if (channel_entry->internal.old_hmacs) {
1853 silc_dlist_start(channel_entry->internal.old_hmacs);
1854 while ((hmac = silc_dlist_get(channel_entry->internal.old_hmacs)))
1855 silc_hmac_free(hmac);
1856 silc_dlist_uninit(channel_entry->internal.old_hmacs);
1858 if (channel_entry->channel_pubkeys)
1859 silc_argument_list_free(channel_entry->channel_pubkeys,
1860 SILC_ARGUMENT_PUBLIC_KEY);
1861 silc_atomic_uninit32(&channel_entry->internal.deleted);
1862 silc_atomic_uninit32(&channel_entry->internal.refcnt);
1863 silc_rwlock_free(channel_entry->internal.lock);
1864 silc_schedule_task_del_by_context(conn->client->schedule, channel_entry);
1865 silc_free(channel_entry);
1868 /* Free channel entry list */
1870 void silc_client_list_free_channels(SilcClient client,
1871 SilcClientConnection conn,
1872 SilcDList channel_list)
1874 SilcChannelEntry channel_entry;
1877 silc_dlist_start(channel_list);
1878 while ((channel_entry = silc_dlist_get(channel_list)))
1879 silc_client_unref_channel(client, conn, channel_entry);
1881 silc_dlist_uninit(channel_list);
1885 /************************* Server Searching Locally *************************/
1887 /* Finds entry for server by the server name. */
1889 SilcServerEntry silc_client_get_server(SilcClient client,
1890 SilcClientConnection conn,
1893 SilcIDCacheEntry id_cache;
1894 SilcServerEntry entry;
1896 if (!client || !conn || !server_name)
1899 SILC_LOG_DEBUG(("Find server by name %s", server_name));
1901 /* Normalize server name for search */
1902 server_name = silc_identifier_check(server_name, strlen(server_name),
1903 SILC_STRING_UTF8, 256, NULL);
1907 silc_mutex_lock(conn->internal->lock);
1909 if (!silc_idcache_find_by_name_one(conn->internal->server_cache,
1910 server_name, &id_cache)) {
1911 silc_free(server_name);
1912 silc_mutex_unlock(conn->internal->lock);
1916 SILC_LOG_DEBUG(("Found"));
1919 entry = id_cache->context;
1920 silc_client_ref_server(client, conn, entry);
1922 silc_mutex_unlock(conn->internal->lock);
1924 silc_free(server_name);
1929 /* Finds entry for server by the server ID. */
1931 SilcServerEntry silc_client_get_server_by_id(SilcClient client,
1932 SilcClientConnection conn,
1933 SilcServerID *server_id)
1935 SilcIDCacheEntry id_cache;
1936 SilcServerEntry entry;
1938 if (!client || !conn || !server_id)
1941 SILC_LOG_DEBUG(("Find server by id %s",
1942 silc_id_render(server_id, SILC_ID_SERVER)));
1944 silc_mutex_lock(conn->internal->lock);
1946 if (!silc_idcache_find_by_id_one(conn->internal->server_cache,
1947 server_id, &id_cache)) {
1948 silc_mutex_unlock(conn->internal->lock);
1952 SILC_LOG_DEBUG(("Found"));
1955 entry = id_cache->context;
1956 silc_client_ref_server(client, conn, entry);
1958 silc_mutex_unlock(conn->internal->lock);
1963 /*********************** Server Resolving from Server ***********************/
1965 /* Resolving context */
1968 SilcGetServerCallback completion;
1970 } *SilcClientGetServerInternal;
1972 /* Resolving command callback */
1974 static SilcBool silc_client_get_server_cb(SilcClient client,
1975 SilcClientConnection conn,
1976 SilcCommand command,
1982 SilcClientGetServerInternal i = context;
1983 SilcServerEntry server;
1985 if (error != SILC_STATUS_OK) {
1986 SILC_LOG_DEBUG(("Resolving failed: %s", silc_get_status_message(error)));
1988 i->completion(client, conn, error, NULL, i->context);
1992 /* Add the returned servers to list */
1993 if (i->completion) {
1994 server = va_arg(ap, SilcServerEntry);
1995 silc_client_ref_server(client, conn, server);
1996 silc_dlist_add(i->servers, server);
1997 server->internal.resolve_cmd_ident = 0;
2000 if (status == SILC_STATUS_OK || status == SILC_STATUS_LIST_END) {
2001 /* Deliver the servers to the caller */
2002 if (i->completion) {
2003 SILC_LOG_DEBUG(("Resolved %d servers", silc_dlist_count(i->servers)));
2004 silc_dlist_start(i->servers);
2005 i->completion(client, conn, SILC_STATUS_OK, i->servers, i->context);
2013 silc_client_list_free_servers(client, conn, i->servers);
2018 /* Resolve server by server ID */
2021 silc_client_get_server_by_id_resolve(SilcClient client,
2022 SilcClientConnection conn,
2023 SilcServerID *server_id,
2024 SilcGetServerCallback completion,
2027 SilcClientGetServerInternal i;
2028 SilcServerEntry server;
2030 SilcUInt16 cmd_ident;
2032 if (!client || !conn || !server_id || !completion)
2035 SILC_LOG_DEBUG(("Resolve server by id %s",
2036 silc_id_render(server_id, SILC_ID_SERVER)));
2038 i = silc_calloc(1, sizeof(*i));
2041 i->completion = completion;
2042 i->context = context;
2043 i->servers = silc_dlist_init();
2049 /* Attach to resolving, if on going */
2050 server = silc_client_get_server_by_id(client, conn, server_id);
2051 if (server && server->internal.resolve_cmd_ident) {
2052 SILC_LOG_DEBUG(("Attach to existing resolving"));
2053 silc_client_unref_server(client, conn, server);
2054 silc_client_command_pending(conn, SILC_COMMAND_NONE,
2055 server->internal.resolve_cmd_ident,
2056 silc_client_get_server_cb, i);
2057 return server->internal.resolve_cmd_ident;
2060 /* Send the command */
2061 idp = silc_id_payload_encode(server_id, SILC_ID_SERVER);
2062 cmd_ident = silc_client_command_send(client, conn, SILC_COMMAND_IDENTIFY,
2063 silc_client_get_server_cb, i, 1,
2064 5, silc_buffer_datalen(idp));
2065 silc_buffer_free(idp);
2066 if (!cmd_ident && completion)
2067 completion(client, conn, SILC_STATUS_ERR_RESOURCE_LIMIT, NULL, context);
2069 if (server && cmd_ident)
2070 server->internal.resolve_cmd_ident = cmd_ident;
2072 silc_client_unref_server(client, conn, server);
2077 /************************** Server Entry Routines ***************************/
2079 /* Add new server entry */
2081 SilcServerEntry silc_client_add_server(SilcClient client,
2082 SilcClientConnection conn,
2083 const char *server_name,
2084 const char *server_info,
2085 SilcServerID *server_id)
2087 SilcServerEntry server_entry;
2088 char *server_namec = NULL;
2093 SILC_LOG_DEBUG(("Adding new server %s", server_name));
2095 server_entry = silc_calloc(1, sizeof(*server_entry));
2099 silc_rwlock_alloc(&server_entry->internal.lock);
2100 silc_atomic_init32(&server_entry->internal.refcnt, 0);
2101 silc_atomic_init32(&server_entry->internal.deleted, 1);
2102 server_entry->id = *server_id;
2104 server_entry->server_name = strdup(server_name);
2106 server_entry->server_info = strdup(server_info);
2108 /* Normalize server name */
2110 server_namec = silc_identifier_check(server_name, strlen(server_name),
2111 SILC_STRING_UTF8, 256, NULL);
2112 if (!server_namec) {
2113 silc_free(server_entry->server_name);
2114 silc_free(server_entry->server_info);
2115 silc_atomic_uninit32(&server_entry->internal.deleted);
2116 silc_atomic_uninit32(&server_entry->internal.refcnt);
2117 silc_rwlock_free(server_entry->internal.lock);
2118 silc_free(server_entry);
2123 silc_mutex_lock(conn->internal->lock);
2125 /* Add server to cache */
2126 if (!silc_idcache_add(conn->internal->server_cache, server_namec,
2127 &server_entry->id, server_entry)) {
2128 silc_free(server_namec);
2129 silc_free(server_entry->server_name);
2130 silc_free(server_entry->server_info);
2131 silc_atomic_uninit32(&server_entry->internal.deleted);
2132 silc_atomic_uninit32(&server_entry->internal.refcnt);
2133 silc_rwlock_free(server_entry->internal.lock);
2134 silc_free(server_entry);
2135 silc_mutex_unlock(conn->internal->lock);
2139 silc_mutex_unlock(conn->internal->lock);
2140 silc_client_ref_server(client, conn, server_entry);
2142 SILC_LOG_DEBUG(("Added %p", server_entry));
2144 return server_entry;
2147 /* Removes server from the cache by the server entry. */
2149 SilcBool silc_client_del_server(SilcClient client, SilcClientConnection conn,
2150 SilcServerEntry server)
2155 if (silc_atomic_sub_int32(&server->internal.deleted, 1) != 0)
2158 silc_client_unref_server(client, conn, server);
2162 /* Updates the `server_entry' with the new information sent as argument. */
2164 void silc_client_update_server(SilcClient client,
2165 SilcClientConnection conn,
2166 SilcServerEntry server_entry,
2167 const char *server_name,
2168 const char *server_info)
2170 char *server_namec = NULL;
2172 SILC_LOG_DEBUG(("Updating server %p", server_entry));
2175 (!server_entry->server_name ||
2176 !silc_utf8_strcasecmp(server_entry->server_name, server_name))) {
2178 server_namec = silc_identifier_check(server_name, strlen(server_name),
2179 SILC_STRING_UTF8, 256, NULL);
2183 silc_free(server_entry->server_name);
2184 server_entry->server_name = strdup(server_name);
2185 if (!server_entry->server_name)
2188 /* Update cache entry */
2189 silc_mutex_lock(conn->internal->lock);
2190 silc_idcache_update_by_context(conn->internal->server_cache, server_entry,
2191 NULL, server_namec, TRUE);
2192 silc_mutex_unlock(conn->internal->lock);
2196 (!server_entry->server_info ||
2197 !silc_utf8_strcasecmp(server_entry->server_info, server_info))) {
2198 silc_free(server_entry->server_info);
2199 server_entry->server_info = strdup(server_info);
2205 void silc_client_lock_server(SilcServerEntry server_entry)
2207 silc_rwlock_rdlock(server_entry->internal.lock);
2212 void silc_client_unlock_server(SilcServerEntry server_entry)
2214 silc_rwlock_unlock(server_entry->internal.lock);
2217 /* Take reference of server entry */
2219 SilcServerEntry silc_client_ref_server(SilcClient client,
2220 SilcClientConnection conn,
2221 SilcServerEntry server_entry)
2223 silc_atomic_add_int32(&server_entry->internal.refcnt, 1);
2224 SILC_LOG_DEBUG(("Server %p refcnt %d->%d", server_entry,
2225 silc_atomic_get_int32(&server_entry->internal.refcnt) - 1,
2226 silc_atomic_get_int32(&server_entry->internal.refcnt)));
2227 return server_entry;
2230 /* Release reference of server entry */
2232 void silc_client_unref_server(SilcClient client, SilcClientConnection conn,
2233 SilcServerEntry server_entry)
2235 SilcIDCacheEntry id_cache;
2241 if (silc_atomic_sub_int32(&server_entry->internal.refcnt, 1) > 0)
2244 SILC_LOG_DEBUG(("Deleting server %p", server_entry));
2246 silc_mutex_lock(conn->internal->lock);
2247 if (silc_idcache_find_by_context(conn->internal->server_cache, server_entry,
2249 namec = id_cache->name;
2250 silc_idcache_del_by_context(conn->internal->server_cache,
2251 server_entry, NULL);
2254 silc_mutex_unlock(conn->internal->lock);
2256 silc_free(server_entry->server_name);
2257 silc_free(server_entry->server_info);
2258 if (server_entry->public_key)
2259 silc_pkcs_public_key_free(server_entry->public_key);
2260 silc_atomic_uninit32(&server_entry->internal.deleted);
2261 silc_atomic_uninit32(&server_entry->internal.refcnt);
2262 silc_rwlock_free(server_entry->internal.lock);
2263 silc_free(server_entry);
2266 /* Free server entry list */
2268 void silc_client_list_free_servers(SilcClient client,
2269 SilcClientConnection conn,
2270 SilcDList server_list)
2272 SilcServerEntry server_entry;
2275 silc_dlist_start(server_list);
2276 while ((server_entry = silc_dlist_get(server_list)))
2277 silc_client_unref_server(client, conn, server_entry);
2279 silc_dlist_uninit(server_list);