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;
131 if (!get_valid || entry->internal.valid) {
132 silc_client_ref_client(client, conn, id_cache->context);
133 silc_dlist_add(clients, id_cache->context);
137 /* Check multiple cache entries for exact match */
138 while ((id_cache = silc_list_get(list))) {
139 entry = id_cache->context;
141 /* If server was provided, find entries that either have no server
142 set or have the same server. Ignore those that have different
144 if (server[0] && entry->server &&
145 !silc_utf8_strcasecmp(entry->server, server))
148 if (silc_utf8_strcasecmp(entry->nickname,
149 format ? format : parsed) &&
150 (!get_valid || entry->internal.valid)) {
151 silc_client_ref_client(client, conn, entry);
152 silc_dlist_add(clients, entry);
154 /* If format is NULL, we find one exact match with the base
155 nickname (parsed). */
162 silc_mutex_unlock(conn->internal->lock);
164 silc_free(nicknamec);
167 if (!silc_dlist_count(clients)) {
168 silc_dlist_uninit(clients);
172 SILC_LOG_DEBUG(("Found %d clients", silc_dlist_count(clients)));
174 silc_dlist_start(clients);
178 /* Finds clients by nickname from local cache. */
180 SilcDList silc_client_get_clients_local(SilcClient client,
181 SilcClientConnection conn,
182 const char *nickname,
185 return silc_client_get_clients_local_ext(client, conn, nickname, return_all,
189 /********************** Client Resolving from Server ************************/
191 /* Resolving context */
194 SilcGetClientCallback completion;
196 SilcClientEntry client_entry;
197 } *SilcClientGetClientInternal;
199 /* Resolving command callback */
201 static SilcBool silc_client_get_clients_cb(SilcClient client,
202 SilcClientConnection conn,
209 SilcClientGetClientInternal i = context;
210 SilcClientEntry client_entry;
212 if (error != SILC_STATUS_OK) {
213 SILC_LOG_DEBUG(("Resolving failed: %s", silc_get_status_message(error)));
215 if (i->client_entry) {
216 i->client_entry->internal.resolve_cmd_ident = 0;
217 silc_client_unref_client(client, conn, i->client_entry);
221 i->completion(client, conn, error, NULL, i->context);
225 /* Add the returned client to list */
227 client_entry = va_arg(ap, SilcClientEntry);
228 silc_client_ref_client(client, conn, client_entry);
229 silc_dlist_add(i->clients, client_entry);
230 client_entry->internal.resolve_cmd_ident = 0;
233 if (status == SILC_STATUS_OK || status == SILC_STATUS_LIST_END) {
234 /* Deliver the clients to the caller */
236 SILC_LOG_DEBUG(("Resolved %d clients", silc_dlist_count(i->clients)));
238 if (i->client_entry) {
239 i->client_entry->internal.resolve_cmd_ident = 0;
240 silc_client_unref_client(client, conn, i->client_entry);
243 silc_dlist_start(i->clients);
244 i->completion(client, conn, SILC_STATUS_OK, i->clients, i->context);
252 silc_client_list_free(client, conn, i->clients);
257 /* Resolves client information from server by the client ID. */
260 silc_client_get_client_by_id_resolve(SilcClient client,
261 SilcClientConnection conn,
262 SilcClientID *client_id,
263 SilcBuffer attributes,
264 SilcGetClientCallback completion,
267 SilcClientGetClientInternal i;
268 SilcClientEntry client_entry;
270 SilcUInt16 cmd_ident;
272 if (!client || !conn | !client_id) {
273 SILC_LOG_ERROR(("Missing arguments to "
274 "silc_client_get_clients_by_id_resolve call"));
278 SILC_LOG_DEBUG(("Resolve client by ID (%s)",
279 silc_id_render(client_id, SILC_ID_CLIENT)));
281 i = silc_calloc(1, sizeof(*i));
284 i->completion = completion;
285 i->context = context;
286 i->clients = silc_dlist_init();
292 /* Attach to resolving, if on going */
293 client_entry = silc_client_get_client_by_id(client, conn, client_id);
294 if (client_entry && client_entry->internal.resolve_cmd_ident) {
295 SILC_LOG_DEBUG(("Attach to existing resolving"));
296 silc_client_unref_client(client, conn, client_entry);
297 silc_client_command_pending(conn, SILC_COMMAND_NONE,
298 client_entry->internal.resolve_cmd_ident,
299 silc_client_get_clients_cb, i);
300 return client_entry->internal.resolve_cmd_ident;
303 /* Send the command */
304 idp = silc_id_payload_encode(client_id, SILC_ID_CLIENT);
305 cmd_ident = silc_client_command_send(client, conn, SILC_COMMAND_WHOIS,
306 silc_client_get_clients_cb, i,
307 2, 3, silc_buffer_datalen(attributes),
308 4, silc_buffer_datalen(idp));
309 if (!cmd_ident && completion)
310 completion(client, conn, SILC_STATUS_ERR_RESOURCE_LIMIT, NULL, context);
312 if (client_entry && cmd_ident) {
313 client_entry->internal.resolve_cmd_ident = cmd_ident;
314 i->client_entry = client_entry;
316 silc_client_unref_client(client, conn, client_entry);
319 silc_buffer_free(idp);
324 /* Finds client entry or entries by the `nickname' and `server'. The
325 completion callback will be called when the client entries has been
326 found. Used internally by the library. */
328 static SilcUInt16 silc_client_get_clients_i(SilcClient client,
329 SilcClientConnection conn,
331 const char *nickname,
333 SilcBuffer attributes,
334 SilcGetClientCallback completion,
337 SilcClientGetClientInternal i;
338 char nick[128 + 1], serv[256 + 1], userhost[768 + 1], *parsed = NULL;
341 SILC_LOG_DEBUG(("Resolve client by %s command",
342 silc_get_command_name(command)));
344 if (!client || !conn) {
345 SILC_LOG_ERROR(("Missing arguments to silc_client_get_clients call"));
348 if (!nickname && !attributes) {
349 SILC_LOG_ERROR(("Missing arguments to silc_client_get_clients call"));
353 /* Parse server name from the nickname if set */
354 if (silc_parse_userfqdn(nickname, nick, sizeof(nick),
355 serv, sizeof(serv)) == 2)
356 server = (const char *)serv;
357 nickname = (const char *)nick;
359 /* Parse nickname in case it is formatted */
360 if (silc_client_nickname_parse(client, conn, (char *)nickname, &parsed))
361 nickname = (const char *)parsed;
363 i = silc_calloc(1, sizeof(*i));
368 i->clients = silc_dlist_init();
374 i->completion = completion;
375 i->context = context;
377 memset(userhost, 0, sizeof(userhost));
378 if (nickname && server) {
379 len = strlen(nickname) + strlen(server) + 3;
380 silc_strncat(userhost, len, nickname, strlen(nickname));
381 silc_strncat(userhost, len, "@", 1);
382 silc_strncat(userhost, len, server, strlen(server));
383 } else if (nickname) {
384 silc_strncat(userhost, sizeof(userhost) - 1, nickname, strlen(nickname));
388 /* Send the command */
389 if (command == SILC_COMMAND_IDENTIFY)
390 return silc_client_command_send(client, conn, SILC_COMMAND_IDENTIFY,
391 silc_client_get_clients_cb, i,
392 1, 1, userhost, strlen(userhost));
393 return silc_client_command_send(client, conn, SILC_COMMAND_WHOIS,
394 silc_client_get_clients_cb, i,
395 2, 1, userhost, strlen(userhost),
396 3, silc_buffer_datalen(attributes));
399 /* Get clients from server with IDENTIFY command */
401 SilcUInt16 silc_client_get_clients(SilcClient client,
402 SilcClientConnection conn,
403 const char *nickname,
405 SilcGetClientCallback completion,
408 return silc_client_get_clients_i(client, conn, SILC_COMMAND_IDENTIFY,
409 nickname, server, NULL,
410 completion, context);
413 /* Get clients from server with WHOIS command */
415 SilcUInt16 silc_client_get_clients_whois(SilcClient client,
416 SilcClientConnection conn,
417 const char *nickname,
419 SilcBuffer attributes,
420 SilcGetClientCallback completion,
423 return silc_client_get_clients_i(client, conn, SILC_COMMAND_WHOIS,
424 nickname, server, attributes,
425 completion, context);
428 /* ID list resolving context */
430 SilcGetClientCallback completion;
432 SilcBuffer client_id_list;
433 SilcUInt32 list_count;
434 } *GetClientsByListInternal;
436 static SilcBool silc_client_get_clients_list_cb(SilcClient client,
437 SilcClientConnection conn,
444 GetClientsByListInternal i = context;
445 SilcClientEntry client_entry;
451 /* Process the list after all replies have been received */
452 if (status != SILC_STATUS_OK && !SILC_STATUS_IS_ERROR(status) &&
453 status != SILC_STATUS_LIST_END)
456 SILC_LOG_DEBUG(("Resolved all clients"));
458 clients = silc_dlist_init();
460 status = SILC_STATUS_ERR_RESOURCE_LIMIT;
464 for (c = 0; c < i->list_count; c++) {
466 SILC_GET16_MSB(idp_len, i->client_id_list->data + 2);
468 if (!silc_id_payload_parse_id(i->client_id_list->data, idp_len, &id)) {
469 status = SILC_STATUS_ERR_BAD_CLIENT_ID;
473 /* Get client entry */
474 client_entry = silc_client_get_client_by_id(client, conn, &id.u.client_id);
476 silc_dlist_add(clients, client_entry);
478 if (!silc_buffer_pull(i->client_id_list, idp_len)) {
479 status = SILC_STATUS_ERR_BAD_CLIENT_ID;
484 silc_dlist_start(clients);
485 status = SILC_STATUS_OK;
487 i->completion(client, conn, status, clients, i->context);
490 if (status != SILC_STATUS_OK && i->completion)
491 i->completion(client, conn, status, NULL, i->context);
493 silc_client_list_free(client, conn, clients);
494 silc_buffer_free(i->client_id_list);
500 /* Gets client entries by the list of client ID's `client_id_list'. This
501 always resolves those client ID's it does not know yet from the server
502 so this function might take a while. The `client_id_list' is a list
503 of ID Payloads added one after other. JOIN command reply and USERS
504 command reply for example returns this sort of list. The `completion'
505 will be called after the entries are available. */
507 SilcUInt16 silc_client_get_clients_by_list(SilcClient client,
508 SilcClientConnection conn,
509 SilcUInt32 list_count,
510 SilcBuffer client_id_list,
511 SilcGetClientCallback completion,
514 GetClientsByListInternal in;
515 SilcClientEntry entry;
516 unsigned char **res_argv = NULL;
517 SilcUInt32 *res_argv_lens = NULL, *res_argv_types = NULL, res_argc = 0;
518 SilcUInt16 idp_len, cmd_ident;
523 SILC_LOG_DEBUG(("Resolve clients from Client ID list"));
525 if (!client || !conn || !client_id_list)
528 in = silc_calloc(1, sizeof(*in));
531 in->completion = completion;
532 in->context = context;
533 in->list_count = list_count;
534 in->client_id_list = silc_buffer_copy(client_id_list);
535 if (!in->client_id_list)
538 for (i = 0; i < list_count; i++) {
540 SILC_GET16_MSB(idp_len, client_id_list->data + 2);
542 if (!silc_id_payload_parse_id(client_id_list->data, idp_len, &id))
545 /* Check if we have this client cached already. If we don't have the
546 entry or it has incomplete info, then resolve it from the server. */
547 entry = silc_client_get_client_by_id(client, conn, &id.u.client_id);
548 if (!entry || !entry->nickname[0] || !entry->username[0] ||
551 res_argv = silc_calloc(list_count, sizeof(*res_argv));
552 res_argv_lens = silc_calloc(list_count, sizeof(*res_argv_lens));
553 res_argv_types = silc_calloc(list_count, sizeof(*res_argv_types));
554 if (!res_argv || !res_argv_lens || !res_argv_types) {
555 silc_client_unref_client(client, conn, entry);
560 res_argv[res_argc] = client_id_list->data;
561 res_argv_lens[res_argc] = idp_len;
562 res_argv_types[res_argc] = res_argc + 4;
565 silc_client_unref_client(client, conn, entry);
567 if (!silc_buffer_pull(client_id_list, idp_len))
570 silc_buffer_start(client_id_list);
572 /* Query the unknown client information from server */
574 cmd_ident = silc_client_command_send_argv(client,
575 conn, SILC_COMMAND_WHOIS,
576 silc_client_get_clients_list_cb,
577 in, res_argc, res_argv,
581 silc_free(res_argv_lens);
582 silc_free(res_argv_types);
586 /* We have the clients in cache, get them and call the completion */
587 silc_client_get_clients_list_cb(client, conn, SILC_COMMAND_WHOIS,
588 SILC_STATUS_OK, SILC_STATUS_OK, in, tmp);
592 silc_buffer_free(in->client_id_list);
595 silc_free(res_argv_lens);
596 silc_free(res_argv_types);
603 SilcClientConnection conn;
604 SilcChannelID channel_id;
605 SilcGetClientCallback completion;
608 } *GetClientsByChannelInternal;
610 SILC_CLIENT_CMD_FUNC(get_clients_by_channel_cb)
612 GetClientsByChannelInternal i = context;
613 SilcClientEntry *clients = NULL;
614 SilcUInt32 clients_count = 0;
615 SilcBool found = FALSE;
616 SilcChannelEntry channel;
617 SilcHashTableList htl;
626 channel = silc_client_get_channel_by_id(i->client, i->conn, &i->channel_id);
627 if (channel && !silc_hash_table_count(channel->user_list)) {
628 clients = silc_calloc(silc_hash_table_count(channel->user_list),
630 silc_hash_table_list(channel->user_list, &htl);
631 while (silc_hash_table_get(&htl, NULL, (void *)&chu))
632 clients[clients_count++] = chu->client;
633 silc_hash_table_list_reset(&htl);
638 i->completion(i->client, i->conn, clients, clients_count, i->context);
641 i->completion(i->client, i->conn, NULL, 0, i->context);
647 /* Gets client entries by the channel entry indicated by `channel'. Thus,
648 it resolves the clients currently on that channel. */
650 void silc_client_get_clients_by_channel(SilcClient client,
651 SilcClientConnection conn,
652 SilcChannelEntry channel,
653 SilcGetClientCallback completion,
656 GetClientsByChannelInternal in;
657 SilcHashTableList htl;
659 SilcClientEntry entry;
660 unsigned char **res_argv = NULL;
661 SilcUInt32 *res_argv_lens = NULL, *res_argv_types = NULL, res_argc = 0;
663 SilcBool wait_res = FALSE;
665 assert(client && conn && channel);
667 SILC_LOG_DEBUG(("Start"));
669 in = silc_calloc(1, sizeof(*in));
672 in->channel_id = *channel->id;
673 in->completion = completion;
674 in->context = context;
676 /* If user list does not exist, send USERS command. */
677 if (!channel->user_list || !silc_hash_table_count(channel->user_list)) {
678 SILC_LOG_DEBUG(("Sending USERS"));
679 silc_client_command_register(client, SILC_COMMAND_USERS, NULL, NULL,
680 silc_client_command_reply_users_i, 0,
682 silc_client_command_send(client, conn, SILC_COMMAND_USERS,
683 conn->cmd_ident, 1, 2, channel->channel_name,
684 strlen(channel->channel_name));
685 silc_client_command_pending(conn, SILC_COMMAND_USERS, conn->cmd_ident,
686 silc_client_command_get_clients_by_channel_cb,
691 silc_hash_table_list(channel->user_list, &htl);
692 while (silc_hash_table_get(&htl, NULL, (void *)&chu)) {
695 /* If the entry has incomplete info, then resolve it from the server. */
696 if (!entry->nickname[0] || !entry->realname) {
697 if (entry->status & SILC_CLIENT_STATUS_RESOLVING) {
698 /* Attach to this resolving and wait until it finishes */
699 silc_client_command_pending(
700 conn, SILC_COMMAND_NONE,
701 entry->resolve_cmd_ident,
702 silc_client_command_get_clients_by_channel_cb,
708 entry->status |= SILC_CLIENT_STATUS_RESOLVING;
709 entry->resolve_cmd_ident = conn->cmd_ident + 1;
711 idp = silc_id_payload_encode(entry->id, SILC_ID_CLIENT);
713 /* No we don't have it, query it from the server. Assemble argument
714 table that will be sent for the WHOIS command later. */
715 res_argv = silc_realloc(res_argv, sizeof(*res_argv) *
717 res_argv_lens = silc_realloc(res_argv_lens, sizeof(*res_argv_lens) *
719 res_argv_types = silc_realloc(res_argv_types, sizeof(*res_argv_types) *
721 res_argv[res_argc] = silc_memdup(idp->data, idp->len);
722 res_argv_lens[res_argc] = idp->len;
723 res_argv_types[res_argc] = res_argc + 4;
726 silc_buffer_free(idp);
729 silc_hash_table_list_reset(&htl);
731 /* Query the client information from server if the list included clients
732 that we don't know about. */
736 /* Send the WHOIS command to server */
737 res_cmd = silc_command_payload_encode(SILC_COMMAND_WHOIS,
738 res_argc, res_argv, res_argv_lens,
739 res_argv_types, ++conn->cmd_ident);
740 silc_client_packet_send(client, conn->sock, SILC_PACKET_COMMAND,
741 NULL, 0, NULL, NULL, res_cmd->data, res_cmd->len,
744 /* Register our own command reply for this command */
745 silc_client_command_register(client, SILC_COMMAND_WHOIS, NULL, NULL,
746 silc_client_command_reply_whois_i, 0,
749 /* Process the applications request after reply has been received */
750 silc_client_command_pending(
751 conn, SILC_COMMAND_WHOIS, conn->cmd_ident,
752 silc_client_command_get_clients_by_channel_cb,
756 silc_buffer_free(res_cmd);
758 silc_free(res_argv_lens);
759 silc_free(res_argv_types);
766 /* We have the clients in cache, get them and call the completion */
767 silc_client_command_get_clients_by_channel_cb((void *)in, NULL);
772 /************************** Client Entry Routines ***************************/
774 /* Creates new client entry and adds it to the ID cache. Returns pointer
777 SilcClientEntry silc_client_add_client(SilcClient client,
778 SilcClientConnection conn,
779 char *nickname, char *username,
780 char *userinfo, SilcClientID *id,
783 SilcClientEntry client_entry;
784 char *nick = NULL, parsed[128 + 1];
786 SILC_LOG_DEBUG(("Adding new client entry"));
788 /* Save the client infos */
789 client_entry = silc_calloc(1, sizeof(*client_entry));
793 silc_rwlock_alloc(&client_entry->internal.lock);
794 silc_atomic_init32(&client_entry->internal.refcnt, 0);
795 silc_atomic_init32(&client_entry->internal.deleted, 1);
796 client_entry->id = *id;
797 client_entry->mode = mode;
798 client_entry->realname = userinfo ? strdup(userinfo) : NULL;
800 silc_parse_userfqdn(nickname, parsed, sizeof(parsed),
801 client_entry->server, sizeof(client_entry->server));
802 if (nickname && client->internal->params->full_nicknames)
803 silc_snprintf(client_entry->nickname, sizeof(client_entry->nickname),
806 silc_snprintf(client_entry->nickname, sizeof(client_entry->nickname),
809 silc_parse_userfqdn(username, client_entry->username,
810 sizeof(client_entry->username),
811 client_entry->hostname,
812 sizeof(client_entry->hostname));
814 client_entry->channels = silc_hash_table_alloc(1, silc_hash_ptr, NULL, NULL,
815 NULL, NULL, NULL, TRUE);
816 if (!client_entry->channels) {
817 silc_free(client_entry->realname);
818 silc_atomic_uninit32(&client_entry->internal.deleted);
819 silc_atomic_uninit32(&client_entry->internal.refcnt);
820 silc_rwlock_free(client_entry->internal.lock);
821 silc_free(client_entry);
825 /* Normalize nickname */
826 if (client_entry->nickname[0]) {
827 nick = silc_identifier_check(parsed, strlen(parsed),
828 SILC_STRING_UTF8, 128, NULL);
830 silc_hash_table_free(client_entry->channels);
831 silc_free(client_entry->realname);
832 silc_atomic_uninit32(&client_entry->internal.deleted);
833 silc_atomic_uninit32(&client_entry->internal.refcnt);
834 silc_rwlock_free(client_entry->internal.lock);
835 silc_free(client_entry);
840 silc_mutex_lock(conn->internal->lock);
842 /* Add client to cache, the normalized nickname is saved to cache */
843 if (!silc_idcache_add(conn->internal->client_cache, nick,
844 &client_entry->id, client_entry)) {
846 silc_hash_table_free(client_entry->channels);
847 silc_free(client_entry->realname);
848 silc_atomic_uninit32(&client_entry->internal.deleted);
849 silc_atomic_uninit32(&client_entry->internal.refcnt);
850 silc_rwlock_free(client_entry->internal.lock);
851 silc_free(client_entry);
852 silc_mutex_unlock(conn->internal->lock);
856 client_entry->nickname_normalized = nick;
858 silc_mutex_unlock(conn->internal->lock);
859 silc_client_ref_client(client, conn, client_entry);
861 /* Format the nickname */
862 silc_client_nickname_format(client, conn, client_entry, FALSE);
864 if (client_entry->nickname[0])
865 client_entry->internal.valid = TRUE;
867 SILC_LOG_DEBUG(("Added %p", client_entry));
872 /* Updates the `client_entry' with the new information sent as argument.
873 This handles entry locking internally. */
875 void silc_client_update_client(SilcClient client,
876 SilcClientConnection conn,
877 SilcClientEntry client_entry,
878 const char *nickname,
879 const char *username,
880 const char *userinfo,
883 char *nick = NULL, parsed[128 + 1];
885 SILC_LOG_DEBUG(("Update client entry"));
887 silc_rwlock_wrlock(client_entry->internal.lock);
889 if (!client_entry->realname && userinfo)
890 client_entry->realname = strdup(userinfo);
892 if ((!client_entry->username[0] || !client_entry->hostname[0]) && username)
893 silc_parse_userfqdn(username, client_entry->username,
894 sizeof(client_entry->username),
895 client_entry->hostname,
896 sizeof(client_entry->username));
898 if (!client_entry->nickname[0] && nickname) {
899 silc_parse_userfqdn(nickname, parsed, sizeof(parsed),
900 client_entry->server, sizeof(client_entry->server));
901 if (client->internal->params->full_nicknames)
902 silc_snprintf(client_entry->nickname, sizeof(client_entry->nickname),
905 silc_snprintf(client_entry->nickname, sizeof(client_entry->nickname),
908 /* Normalize nickname */
909 nick = silc_identifier_check(parsed, strlen(parsed),
910 SILC_STRING_UTF8, 128, NULL);
912 silc_rwlock_unlock(client_entry->internal.lock);
916 /* Format nickname */
917 silc_client_nickname_format(client, conn, client_entry,
918 client_entry == conn->local_entry);
920 /* Update cache entry */
921 silc_mutex_lock(conn->internal->lock);
922 silc_idcache_update_by_context(conn->internal->client_cache,
923 client_entry, NULL, nick, TRUE);
924 silc_mutex_unlock(conn->internal->lock);
925 client_entry->nickname_normalized = nick;
926 client_entry->internal.valid = TRUE;
928 client_entry->mode = mode;
930 silc_rwlock_unlock(client_entry->internal.lock);
933 /* Change a client's nickname. Must be called with `client_entry' locked. */
935 SilcBool silc_client_change_nickname(SilcClient client,
936 SilcClientConnection conn,
937 SilcClientEntry client_entry,
938 const char *new_nick,
939 SilcClientID *new_id,
940 const unsigned char *idp,
945 SILC_LOG_DEBUG(("Change nickname %s to %s", client_entry->nickname,
948 /* Normalize nickname */
949 tmp = silc_identifier_check(new_nick, strlen(new_nick),
950 SILC_STRING_UTF8, 128, NULL);
954 /* Update the client entry */
955 silc_mutex_lock(conn->internal->lock);
956 if (!silc_idcache_update_by_context(conn->internal->client_cache,
957 client_entry, new_id, tmp, TRUE)) {
959 silc_mutex_unlock(conn->internal->lock);
962 silc_mutex_unlock(conn->internal->lock);
964 memset(client_entry->nickname, 0, sizeof(client_entry->nickname));
965 memcpy(client_entry->nickname, new_nick, strlen(new_nick));
966 client_entry->nickname_normalized = tmp;
967 silc_client_nickname_format(client, conn, client_entry,
968 client_entry == conn->local_entry);
970 /* For my client entry, update ID and set new ID to packet stream */
971 if (client_entry == conn->local_entry) {
972 if (idp && idp_len) {
973 silc_buffer_enlarge(conn->internal->local_idp, idp_len);
974 silc_buffer_put(conn->internal->local_idp, idp, idp_len);
977 silc_packet_set_ids(conn->stream, SILC_ID_CLIENT, conn->local_id,
981 client_entry->internal.valid = TRUE;
985 /* Deletes the client entry and frees all memory. */
987 void silc_client_del_client_entry(SilcClient client,
988 SilcClientConnection conn,
989 SilcClientEntry client_entry)
991 silc_free(client_entry->realname);
992 silc_free(client_entry->nickname_normalized);
993 silc_free(client_entry->internal.key);
994 if (client_entry->public_key)
995 silc_pkcs_public_key_free(client_entry->public_key);
996 silc_hash_table_free(client_entry->channels);
997 if (client_entry->internal.send_key)
998 silc_cipher_free(client_entry->internal.send_key);
999 if (client_entry->internal.receive_key)
1000 silc_cipher_free(client_entry->internal.receive_key);
1001 if (client_entry->internal.hmac_send)
1002 silc_hmac_free(client_entry->internal.hmac_send);
1003 if (client_entry->internal.hmac_receive)
1004 silc_hmac_free(client_entry->internal.hmac_receive);
1005 silc_client_ftp_session_free_client(client, client_entry);
1006 if (client_entry->internal.ke)
1007 silc_client_abort_key_agreement(client, conn, client_entry);
1008 silc_atomic_uninit32(&client_entry->internal.deleted);
1009 silc_atomic_uninit32(&client_entry->internal.refcnt);
1010 silc_rwlock_free(client_entry->internal.lock);
1011 silc_free(client_entry);
1014 /* Removes client from the cache by the client entry. */
1016 SilcBool silc_client_del_client(SilcClient client, SilcClientConnection conn,
1017 SilcClientEntry client_entry)
1022 SILC_LOG_DEBUG(("Marking client entry %p deleted"));
1024 if (silc_atomic_sub_int32(&client_entry->internal.deleted, 1) != 0) {
1025 SILC_LOG_DEBUG(("Client entry %p already marked deleted"));
1029 silc_client_unref_client(client, conn, client_entry);
1033 /* Internal routine used to find client by ID and if not found this creates
1034 new client entry and returns it. */
1036 SilcClientEntry silc_client_get_client(SilcClient client,
1037 SilcClientConnection conn,
1038 SilcClientID *client_id)
1040 SilcClientEntry client_entry;
1042 client_entry = silc_client_get_client_by_id(client, conn, client_id);
1043 if (!client_entry) {
1044 client_entry = silc_client_add_client(client, conn, NULL, NULL, NULL,
1048 silc_client_ref_client(client, conn, client_entry);
1051 return client_entry;
1056 void silc_client_lock_client(SilcClientEntry client_entry)
1058 silc_rwlock_rdlock(client_entry->internal.lock);
1063 void silc_client_unlock_client(SilcClientEntry client_entry)
1065 silc_rwlock_unlock(client_entry->internal.lock);
1068 /* Take reference of client entry */
1070 SilcClientEntry silc_client_ref_client(SilcClient client,
1071 SilcClientConnection conn,
1072 SilcClientEntry client_entry)
1074 silc_atomic_add_int32(&client_entry->internal.refcnt, 1);
1075 SILC_LOG_DEBUG(("Client %p refcnt %d->%d", client_entry,
1076 silc_atomic_get_int32(&client_entry->internal.refcnt) - 1,
1077 silc_atomic_get_int32(&client_entry->internal.refcnt)));
1078 return client_entry;
1081 /* Release reference of client entry */
1083 void silc_client_unref_client(SilcClient client, SilcClientConnection conn,
1084 SilcClientEntry client_entry)
1091 SILC_LOG_DEBUG(("Client %p refcnt %d->%d", client_entry,
1092 silc_atomic_get_int32(&client_entry->internal.refcnt),
1093 silc_atomic_get_int32(&client_entry->internal.refcnt) - 1));
1095 if (silc_atomic_sub_int32(&client_entry->internal.refcnt, 1) > 0)
1098 SILC_LOG_DEBUG(("Deleting client %p (%d)", client_entry,
1099 silc_atomic_get_int32(&client_entry->internal.deleted)));
1101 silc_mutex_lock(conn->internal->lock);
1102 ret = silc_idcache_del_by_context(conn->internal->client_cache,
1103 client_entry, NULL);
1104 silc_mutex_unlock(conn->internal->lock);
1107 /* Remove from channels */
1108 silc_client_remove_from_channels(client, conn, client_entry);
1110 /* Free the client entry data */
1111 silc_client_del_client_entry(client, conn, client_entry);
1115 /* Free client entry list */
1117 void silc_client_list_free(SilcClient client, SilcClientConnection conn,
1118 SilcDList client_list)
1120 SilcClientEntry client_entry;
1123 silc_dlist_start(client_list);
1124 while ((client_entry = silc_dlist_get(client_list)))
1125 silc_client_unref_client(client, conn, client_entry);
1127 silc_dlist_uninit(client_list);
1131 /* Formats the nickname of the client specified by the `client_entry'.
1132 If the format is specified by the application this will format the
1133 nickname and replace the old nickname in the client entry. If the
1134 format string is not specified then this function has no effect.
1135 Returns the client entry that was formatted. */
1137 SilcClientEntry silc_client_nickname_format(SilcClient client,
1138 SilcClientConnection conn,
1139 SilcClientEntry client_entry,
1143 char newnick[128 + 1];
1144 int i, off = 0, len;
1146 SilcClientEntry entry, unformatted = NULL;
1147 SilcBool formatted = FALSE;
1149 if (!client->internal->params->nickname_format[0])
1150 return client_entry;
1151 if (!client_entry->nickname[0])
1154 SILC_LOG_DEBUG(("Format nickname"));
1156 /* Get all clients with same nickname. Do not perform the formatting
1157 if there aren't any clients with same nickname unless the application
1158 is forcing us to do so. */
1159 clients = silc_client_get_clients_local_ext(client, conn,
1160 client_entry->nickname,
1164 if (silc_dlist_count(clients) == 1 && !priority &&
1165 !client->internal->params->nickname_force_format) {
1166 silc_client_list_free(client, conn, clients);
1167 return client_entry;
1170 /* Is the requested client formatted already */
1171 if (client_entry->nickname_normalized &&
1172 !silc_utf8_strcasecmp(client_entry->nickname,
1173 client_entry->nickname_normalized))
1176 if (client->internal->params->nickname_force_format)
1179 /* Find unformatted client entry */
1180 while ((entry = silc_dlist_get(clients))) {
1181 if (!entry->internal.valid)
1183 if (entry == client_entry)
1185 if (silc_utf8_strcasecmp(entry->nickname, entry->nickname_normalized)) {
1186 unformatted = entry;
1191 /* If there are no other unformatted clients and the requested client is
1192 unformatted, just return it. */
1193 if (!unformatted && !formatted) {
1194 silc_client_list_free(client, conn, clients);
1195 return client_entry;
1198 /* If priority formatting then the requested client will get the
1199 unformatted nickname, and the unformatted client will get a new
1200 formatted nickname. */
1203 /* Simply change the client's nickname to unformatted */
1204 if (!silc_client_nickname_parse(client, conn, client_entry->nickname,
1208 silc_snprintf(client_entry->nickname, sizeof(client_entry->nickname),
1214 /* There was no other unformatted client */
1215 silc_client_list_free(client, conn, clients);
1216 return client_entry;
1219 /* Now format the previously unformatted client */
1220 client_entry = unformatted;
1224 /* If already formatted just return it */
1226 silc_client_list_free(client, conn, clients);
1227 return client_entry;
1230 memset(newnick, 0, sizeof(newnick));
1231 cp = client->internal->params->nickname_format;
1241 if (!client_entry->nickname[0])
1243 len = strlen(client_entry->nickname);
1244 memcpy(&newnick[off], client_entry->nickname, len);
1248 /* Stripped hostname */
1249 if (!client_entry->hostname[0])
1251 len = strcspn(client_entry->hostname, ".");
1252 i = strcspn(client_entry->hostname, "-");
1255 memcpy(&newnick[off], client_entry->hostname, len);
1260 if (!client_entry->hostname[0])
1262 len = strlen(client_entry->hostname);
1263 memcpy(&newnick[off], client_entry->hostname, len);
1267 /* Ascending number */
1272 if (silc_dlist_count(clients) == 1)
1275 silc_dlist_start(clients);
1276 while ((entry = silc_dlist_get(clients))) {
1277 if (!silc_utf8_strncasecmp(entry->nickname, newnick, off))
1279 if (strlen(entry->nickname) <= off)
1281 num = atoi(&entry->nickname[off]);
1286 memset(tmp, 0, sizeof(tmp));
1287 silc_snprintf(tmp, sizeof(tmp) - 1, "%d", ++max);
1289 memcpy(&newnick[off], tmp, len);
1294 /* Some other character in the string */
1295 memcpy(&newnick[off], cp, 1);
1304 memset(client_entry->nickname, 0, sizeof(client_entry->nickname));
1305 memcpy(client_entry->nickname, newnick, strlen(newnick));
1306 silc_client_list_free(client, conn, clients);
1308 return client_entry;
1311 /* Parses nickname according to nickname format string */
1313 SilcBool silc_client_nickname_parse(SilcClient client,
1314 SilcClientConnection conn,
1318 char *cp, s = 0, e = 0, *nick;
1322 if (!client->internal->params->nickname_format[0]) {
1327 if (!nickname || !nickname[0])
1330 cp = client->internal->params->nickname_format;
1348 /* Get separator character */
1361 /* Parse the nickname */
1365 if (strchr(nickname, s))
1366 nick = strchr(nickname, s) + 1;
1368 if (strchr(nick, e))
1369 len = strchr(nick, e) - nick;
1373 *ret_nick = silc_memdup(nick, len);
1377 SILC_LOG_DEBUG(("Parsed nickname: %s", *ret_nick));
1382 /************************ Channel Searching Locally *************************/
1384 /* Finds entry for channel by the channel name. Returns the entry or NULL
1385 if the entry was not found. It is found only if the client is joined
1388 SilcChannelEntry silc_client_get_channel(SilcClient client,
1389 SilcClientConnection conn,
1393 SilcIDCacheEntry id_cache;
1394 SilcChannelEntry entry = NULL;
1395 char chname[256 + 1], server[256 + 1];
1397 if (!client || !conn || !channel)
1400 SILC_LOG_DEBUG(("Find channel %s", channel));
1402 /* Parse server name from channel name */
1403 silc_parse_userfqdn(channel, chname, sizeof(chname), server, sizeof(server));
1405 /* Normalize name for search */
1406 channel = silc_channel_name_check(chname, strlen(chname), SILC_STRING_UTF8,
1411 silc_mutex_lock(conn->internal->lock);
1413 if (!silc_idcache_find_by_name(conn->internal->channel_cache, channel,
1415 silc_mutex_unlock(conn->internal->lock);
1420 /* If server name was specified with channel name, find the correct
1421 channel entry with the server name. There can only be one channel
1422 with same name on same server. */
1423 silc_list_start(list);
1425 while ((id_cache = silc_list_get(list))) {
1426 entry = id_cache->context;
1427 if (!entry->server[0])
1429 if (silc_utf8_strcasecmp(entry->server, server))
1433 /* Get first channel without server name specified or one with our
1434 current server connection name */
1435 while ((id_cache = silc_list_get(list))) {
1436 entry = id_cache->context;
1437 if (!entry->server[0])
1439 if (silc_utf8_strcasecmp(entry->server, conn->remote_host))
1445 silc_mutex_unlock(conn->internal->lock);
1450 SILC_LOG_DEBUG(("Found channel %s%s%s", entry->channel_name,
1451 entry->server[0] ? "@" : "", entry->server));
1454 silc_client_ref_channel(client, conn, entry);
1455 silc_mutex_unlock(conn->internal->lock);
1462 /* Finds entry for channel by the channel ID. Returns the entry or NULL
1463 if the entry was not found. It is found only if the client is joined
1466 SilcChannelEntry silc_client_get_channel_by_id(SilcClient client,
1467 SilcClientConnection conn,
1468 SilcChannelID *channel_id)
1470 SilcIDCacheEntry id_cache;
1471 SilcChannelEntry entry;
1473 if (!client || !conn || !channel_id)
1476 SILC_LOG_DEBUG(("Find channel by id %s",
1477 silc_id_render(channel_id, SILC_ID_CHANNEL)));
1479 silc_mutex_lock(conn->internal->lock);
1481 if (!silc_idcache_find_by_id_one(conn->internal->channel_cache, channel_id,
1483 silc_mutex_unlock(conn->internal->lock);
1487 SILC_LOG_DEBUG(("Found"));
1489 entry = id_cache->context;
1492 silc_client_ref_channel(client, conn, entry);
1493 silc_mutex_unlock(conn->internal->lock);
1498 /********************** Channel Resolving from Server ***********************/
1500 /* Channel resolving context */
1503 SilcGetChannelCallback completion;
1505 } *SilcClientGetChannelInternal;
1507 /* Resolving command callback */
1509 static SilcBool silc_client_get_channel_cb(SilcClient client,
1510 SilcClientConnection conn,
1511 SilcCommand command,
1517 SilcClientGetChannelInternal i = context;
1518 SilcChannelEntry entry;
1520 if (error != SILC_STATUS_OK) {
1521 SILC_LOG_DEBUG(("Resolving failed: %s", silc_get_status_message(error)));
1523 i->completion(client, conn, error, NULL, i->context);
1527 /* Add the returned channel to list */
1528 if (i->completion) {
1529 entry = va_arg(ap, SilcChannelEntry);
1530 silc_client_ref_channel(client, conn, entry);
1531 silc_dlist_add(i->channels, entry);
1534 if (status == SILC_STATUS_OK || status == SILC_STATUS_LIST_END) {
1535 /* Deliver the channels to the caller */
1536 if (i->completion) {
1537 SILC_LOG_DEBUG(("Resolved %d channels", silc_dlist_count(i->channels)));
1538 silc_dlist_start(i->channels);
1539 i->completion(client, conn, SILC_STATUS_OK, i->channels, i->context);
1547 silc_client_list_free_channels(client, conn, i->channels);
1552 /* Resolves channel entry from the server by the channel name. */
1554 void silc_client_get_channel_resolve(SilcClient client,
1555 SilcClientConnection conn,
1557 SilcGetChannelCallback completion,
1560 SilcClientGetChannelInternal i;
1562 if (!client || !conn || !channel_name || !completion)
1565 SILC_LOG_DEBUG(("Resolve channel %s", channel_name));
1567 i = silc_calloc(1, sizeof(*i));
1570 i->completion = completion;
1571 i->context = context;
1572 i->channels = silc_dlist_init();
1578 /* Send the command */
1579 if (!silc_client_command_send(client, conn, SILC_COMMAND_IDENTIFY,
1580 silc_client_get_channel_cb, i, 1,
1581 3, channel_name, strlen(channel_name))) {
1583 completion(client, conn, SILC_STATUS_ERR_RESOURCE_LIMIT, NULL, context);
1587 /* Resolves channel information from the server by the channel ID. */
1590 silc_client_get_channel_by_id_resolve(SilcClient client,
1591 SilcClientConnection conn,
1592 SilcChannelID *channel_id,
1593 SilcGetChannelCallback completion,
1596 SilcClientGetChannelInternal i;
1598 SilcUInt16 cmd_ident;
1600 if (!client || !conn || !channel_id || !completion)
1603 SILC_LOG_DEBUG(("Resolve channel by id %s",
1604 silc_id_render(channel_id, SILC_ID_CHANNEL)));
1606 i = silc_calloc(1, sizeof(*i));
1609 i->completion = completion;
1610 i->context = context;
1611 i->channels = silc_dlist_init();
1617 /* Send the command */
1618 idp = silc_id_payload_encode(channel_id, SILC_ID_CHANNEL);
1619 cmd_ident = silc_client_command_send(client, conn, SILC_COMMAND_IDENTIFY,
1620 silc_client_get_channel_cb, i, 1,
1621 5, silc_buffer_datalen(idp));
1622 silc_buffer_free(idp);
1623 if (!cmd_ident && completion)
1624 completion(client, conn, SILC_STATUS_ERR_RESOURCE_LIMIT, NULL, context);
1629 /************************* Channel Entry Routines ***************************/
1631 /* Add new channel entry to the ID Cache */
1633 SilcChannelEntry silc_client_add_channel(SilcClient client,
1634 SilcClientConnection conn,
1635 const char *channel_name,
1637 SilcChannelID *channel_id)
1639 SilcChannelEntry channel;
1640 char *channel_namec, name[256 + 1];
1642 SILC_LOG_DEBUG(("Adding channel %s", channel_name));
1644 channel = silc_calloc(1, sizeof(*channel));
1648 silc_rwlock_alloc(&channel->internal.lock);
1649 silc_atomic_init32(&channel->internal.refcnt, 0);
1650 silc_atomic_init32(&channel->internal.deleted, 1);
1651 channel->id = *channel_id;
1652 channel->mode = mode;
1654 silc_parse_userfqdn(channel_name, name, sizeof(name),
1655 channel->server, sizeof(channel->server));
1656 if (client->internal->params->full_channel_names)
1657 channel->channel_name = strdup(channel_name);
1659 channel->channel_name = strdup(name);
1661 if (!channel->channel_name) {
1662 silc_rwlock_free(channel->internal.lock);
1663 silc_atomic_uninit32(&channel->internal.refcnt);
1664 silc_atomic_uninit32(&channel->internal.deleted);
1669 channel->user_list = silc_hash_table_alloc(1, silc_hash_ptr, NULL, NULL,
1670 NULL, NULL, NULL, TRUE);
1671 if (!channel->user_list) {
1672 silc_rwlock_free(channel->internal.lock);
1673 silc_atomic_uninit32(&channel->internal.refcnt);
1674 silc_atomic_uninit32(&channel->internal.deleted);
1675 silc_free(channel->channel_name);
1680 /* Normalize channel name */
1681 channel_namec = silc_channel_name_check(name, strlen(name),
1682 SILC_STRING_UTF8, 256, NULL);
1683 if (!channel_namec) {
1684 silc_rwlock_free(channel->internal.lock);
1685 silc_atomic_uninit32(&channel->internal.refcnt);
1686 silc_atomic_uninit32(&channel->internal.deleted);
1687 silc_free(channel->channel_name);
1688 silc_hash_table_free(channel->user_list);
1693 silc_mutex_lock(conn->internal->lock);
1695 /* Add channel to cache, the normalized channel name is saved to cache */
1696 if (!silc_idcache_add(conn->internal->channel_cache, channel_namec,
1697 &channel->id, channel)) {
1698 silc_rwlock_free(channel->internal.lock);
1699 silc_atomic_uninit32(&channel->internal.refcnt);
1700 silc_atomic_uninit32(&channel->internal.deleted);
1701 silc_free(channel_namec);
1702 silc_free(channel->channel_name);
1703 silc_hash_table_free(channel->user_list);
1705 silc_mutex_unlock(conn->internal->lock);
1709 silc_mutex_unlock(conn->internal->lock);
1710 silc_client_ref_channel(client, conn, channel);
1712 SILC_LOG_DEBUG(("Added %p", channel));
1717 /* Removes channel from the cache by the channel entry. */
1719 SilcBool silc_client_del_channel(SilcClient client, SilcClientConnection conn,
1720 SilcChannelEntry channel)
1725 SILC_LOG_DEBUG(("Marking channel entry %p deleted"));
1727 if (silc_atomic_sub_int32(&channel->internal.deleted, 1) != 0) {
1728 SILC_LOG_DEBUG(("Channel entry %p already marked deleted"));
1732 silc_client_unref_channel(client, conn, channel);
1736 /* Replaces the channel ID of the `channel' to `new_id'. Returns FALSE
1737 if the ID could not be changed. This handles entry locking internally. */
1739 SilcBool silc_client_replace_channel_id(SilcClient client,
1740 SilcClientConnection conn,
1741 SilcChannelEntry channel,
1742 SilcChannelID *new_id)
1744 SilcBool ret = FALSE;
1749 SILC_LOG_DEBUG(("Old Channel ID id(%s)",
1750 silc_id_render(&channel->id, SILC_ID_CHANNEL)));
1751 SILC_LOG_DEBUG(("New Channel ID id(%s)",
1752 silc_id_render(new_id, SILC_ID_CHANNEL)));
1755 silc_rwlock_wrlock(channel->internal.lock);
1756 silc_mutex_lock(conn->internal->lock);
1757 silc_idcache_update_by_context(conn->internal->channel_cache, channel,
1758 new_id, NULL, FALSE);
1759 silc_mutex_unlock(conn->internal->lock);
1760 silc_rwlock_unlock(channel->internal.lock);
1767 void silc_client_lock_channel(SilcChannelEntry channel_entry)
1769 silc_rwlock_rdlock(channel_entry->internal.lock);
1772 /* Unlock channel */
1774 void silc_client_unlock_channel(SilcChannelEntry channel_entry)
1776 silc_rwlock_unlock(channel_entry->internal.lock);
1779 /* Take reference of channel entry */
1781 SilcChannelEntry silc_client_ref_channel(SilcClient client,
1782 SilcClientConnection conn,
1783 SilcChannelEntry channel_entry)
1785 silc_atomic_add_int32(&channel_entry->internal.refcnt, 1);
1786 SILC_LOG_DEBUG(("Channel %p refcnt %d->%d", channel_entry,
1787 silc_atomic_get_int32(&channel_entry->internal.refcnt) - 1,
1788 silc_atomic_get_int32(&channel_entry->internal.refcnt)));
1789 return channel_entry;
1792 /* Release reference of channel entry */
1794 void silc_client_unref_channel(SilcClient client, SilcClientConnection conn,
1795 SilcChannelEntry channel_entry)
1797 SilcIDCacheEntry id_cache;
1798 SilcBool ret = TRUE;
1806 SILC_LOG_DEBUG(("Channel %p refcnt %d->%d", channel_entry,
1807 silc_atomic_get_int32(&channel_entry->internal.refcnt),
1808 silc_atomic_get_int32(&channel_entry->internal.refcnt)
1811 if (silc_atomic_sub_int32(&channel_entry->internal.refcnt, 1) > 0)
1814 SILC_LOG_DEBUG(("Deleting channel %p", channel_entry));
1816 silc_mutex_lock(conn->internal->lock);
1817 if (silc_idcache_find_by_context(conn->internal->channel_cache, channel_entry,
1819 namec = id_cache->name;
1820 ret = silc_idcache_del_by_context(conn->internal->channel_cache,
1821 channel_entry, NULL);
1824 silc_mutex_unlock(conn->internal->lock);
1829 silc_client_empty_channel(client, conn, channel_entry);
1830 silc_client_del_channel_private_keys(client, conn, channel_entry);
1831 silc_hash_table_free(channel_entry->user_list);
1832 silc_free(channel_entry->channel_name);
1833 silc_free(channel_entry->topic);
1834 if (channel_entry->founder_key)
1835 silc_pkcs_public_key_free(channel_entry->founder_key);
1836 if (channel_entry->internal.send_key)
1837 silc_cipher_free(channel_entry->internal.send_key);
1838 if (channel_entry->internal.receive_key)
1839 silc_cipher_free(channel_entry->internal.receive_key);
1840 if (channel_entry->internal.hmac)
1841 silc_hmac_free(channel_entry->internal.hmac);
1842 if (channel_entry->internal.old_channel_keys) {
1843 silc_dlist_start(channel_entry->internal.old_channel_keys);
1844 while ((key = silc_dlist_get(channel_entry->internal.old_channel_keys)))
1845 silc_cipher_free(key);
1846 silc_dlist_uninit(channel_entry->internal.old_channel_keys);
1848 if (channel_entry->internal.old_hmacs) {
1849 silc_dlist_start(channel_entry->internal.old_hmacs);
1850 while ((hmac = silc_dlist_get(channel_entry->internal.old_hmacs)))
1851 silc_hmac_free(hmac);
1852 silc_dlist_uninit(channel_entry->internal.old_hmacs);
1854 if (channel_entry->channel_pubkeys)
1855 silc_argument_list_free(channel_entry->channel_pubkeys,
1856 SILC_ARGUMENT_PUBLIC_KEY);
1857 silc_atomic_uninit32(&channel_entry->internal.deleted);
1858 silc_atomic_uninit32(&channel_entry->internal.refcnt);
1859 silc_rwlock_free(channel_entry->internal.lock);
1860 silc_schedule_task_del_by_context(conn->client->schedule, channel_entry);
1861 silc_free(channel_entry);
1864 /* Free channel entry list */
1866 void silc_client_list_free_channels(SilcClient client,
1867 SilcClientConnection conn,
1868 SilcDList channel_list)
1870 SilcChannelEntry channel_entry;
1873 silc_dlist_start(channel_list);
1874 while ((channel_entry = silc_dlist_get(channel_list)))
1875 silc_client_unref_channel(client, conn, channel_entry);
1877 silc_dlist_uninit(channel_list);
1881 /************************* Server Searching Locally *************************/
1883 /* Finds entry for server by the server name. */
1885 SilcServerEntry silc_client_get_server(SilcClient client,
1886 SilcClientConnection conn,
1889 SilcIDCacheEntry id_cache;
1890 SilcServerEntry entry;
1892 if (!client || !conn || !server_name)
1895 SILC_LOG_DEBUG(("Find server by name %s", server_name));
1897 /* Normalize server name for search */
1898 server_name = silc_identifier_check(server_name, strlen(server_name),
1899 SILC_STRING_UTF8, 256, NULL);
1903 silc_mutex_lock(conn->internal->lock);
1905 if (!silc_idcache_find_by_name_one(conn->internal->server_cache,
1906 server_name, &id_cache)) {
1907 silc_free(server_name);
1908 silc_mutex_unlock(conn->internal->lock);
1912 SILC_LOG_DEBUG(("Found"));
1915 entry = id_cache->context;
1916 silc_client_ref_server(client, conn, entry);
1918 silc_mutex_unlock(conn->internal->lock);
1920 silc_free(server_name);
1925 /* Finds entry for server by the server ID. */
1927 SilcServerEntry silc_client_get_server_by_id(SilcClient client,
1928 SilcClientConnection conn,
1929 SilcServerID *server_id)
1931 SilcIDCacheEntry id_cache;
1932 SilcServerEntry entry;
1934 if (!client || !conn || !server_id)
1937 SILC_LOG_DEBUG(("Find server by id %s",
1938 silc_id_render(server_id, SILC_ID_SERVER)));
1940 silc_mutex_lock(conn->internal->lock);
1942 if (!silc_idcache_find_by_id_one(conn->internal->server_cache,
1943 server_id, &id_cache)) {
1944 silc_mutex_unlock(conn->internal->lock);
1948 SILC_LOG_DEBUG(("Found"));
1951 entry = id_cache->context;
1952 silc_client_ref_server(client, conn, entry);
1954 silc_mutex_unlock(conn->internal->lock);
1959 /*********************** Server Resolving from Server ***********************/
1961 /* Resolving context */
1964 SilcGetServerCallback completion;
1966 } *SilcClientGetServerInternal;
1968 /* Resolving command callback */
1970 static SilcBool silc_client_get_server_cb(SilcClient client,
1971 SilcClientConnection conn,
1972 SilcCommand command,
1978 SilcClientGetServerInternal i = context;
1979 SilcServerEntry server;
1981 if (error != SILC_STATUS_OK) {
1982 SILC_LOG_DEBUG(("Resolving failed: %s", silc_get_status_message(error)));
1984 i->completion(client, conn, error, NULL, i->context);
1988 /* Add the returned servers to list */
1989 if (i->completion) {
1990 server = va_arg(ap, SilcServerEntry);
1991 silc_client_ref_server(client, conn, server);
1992 silc_dlist_add(i->servers, server);
1993 server->internal.resolve_cmd_ident = 0;
1996 if (status == SILC_STATUS_OK || status == SILC_STATUS_LIST_END) {
1997 /* Deliver the servers to the caller */
1998 if (i->completion) {
1999 SILC_LOG_DEBUG(("Resolved %d servers", silc_dlist_count(i->servers)));
2000 silc_dlist_start(i->servers);
2001 i->completion(client, conn, SILC_STATUS_OK, i->servers, i->context);
2009 silc_client_list_free_servers(client, conn, i->servers);
2014 /* Resolve server by server ID */
2017 silc_client_get_server_by_id_resolve(SilcClient client,
2018 SilcClientConnection conn,
2019 SilcServerID *server_id,
2020 SilcGetServerCallback completion,
2023 SilcClientGetServerInternal i;
2024 SilcServerEntry server;
2026 SilcUInt16 cmd_ident;
2028 if (!client || !conn || !server_id || !completion)
2031 SILC_LOG_DEBUG(("Resolve server by id %s",
2032 silc_id_render(server_id, SILC_ID_SERVER)));
2034 i = silc_calloc(1, sizeof(*i));
2037 i->completion = completion;
2038 i->context = context;
2039 i->servers = silc_dlist_init();
2045 /* Attach to resolving, if on going */
2046 server = silc_client_get_server_by_id(client, conn, server_id);
2047 if (server && server->internal.resolve_cmd_ident) {
2048 SILC_LOG_DEBUG(("Attach to existing resolving"));
2049 silc_client_unref_server(client, conn, server);
2050 silc_client_command_pending(conn, SILC_COMMAND_NONE,
2051 server->internal.resolve_cmd_ident,
2052 silc_client_get_server_cb, i);
2053 return server->internal.resolve_cmd_ident;
2056 /* Send the command */
2057 idp = silc_id_payload_encode(server_id, SILC_ID_SERVER);
2058 cmd_ident = silc_client_command_send(client, conn, SILC_COMMAND_IDENTIFY,
2059 silc_client_get_server_cb, i, 1,
2060 5, silc_buffer_datalen(idp));
2061 silc_buffer_free(idp);
2062 if (!cmd_ident && completion)
2063 completion(client, conn, SILC_STATUS_ERR_RESOURCE_LIMIT, NULL, context);
2065 if (server && cmd_ident)
2066 server->internal.resolve_cmd_ident = cmd_ident;
2068 silc_client_unref_server(client, conn, server);
2073 /************************** Server Entry Routines ***************************/
2075 /* Add new server entry */
2077 SilcServerEntry silc_client_add_server(SilcClient client,
2078 SilcClientConnection conn,
2079 const char *server_name,
2080 const char *server_info,
2081 SilcServerID *server_id)
2083 SilcServerEntry server_entry;
2084 char *server_namec = NULL;
2089 SILC_LOG_DEBUG(("Adding new server %s", server_name));
2091 server_entry = silc_calloc(1, sizeof(*server_entry));
2095 silc_rwlock_alloc(&server_entry->internal.lock);
2096 silc_atomic_init32(&server_entry->internal.refcnt, 0);
2097 silc_atomic_init32(&server_entry->internal.deleted, 1);
2098 server_entry->id = *server_id;
2100 server_entry->server_name = strdup(server_name);
2102 server_entry->server_info = strdup(server_info);
2104 /* Normalize server name */
2106 server_namec = silc_identifier_check(server_name, strlen(server_name),
2107 SILC_STRING_UTF8, 256, NULL);
2108 if (!server_namec) {
2109 silc_free(server_entry->server_name);
2110 silc_free(server_entry->server_info);
2111 silc_atomic_uninit32(&server_entry->internal.deleted);
2112 silc_atomic_uninit32(&server_entry->internal.refcnt);
2113 silc_rwlock_free(server_entry->internal.lock);
2114 silc_free(server_entry);
2119 silc_mutex_lock(conn->internal->lock);
2121 /* Add server to cache */
2122 if (!silc_idcache_add(conn->internal->server_cache, server_namec,
2123 &server_entry->id, server_entry)) {
2124 silc_free(server_namec);
2125 silc_free(server_entry->server_name);
2126 silc_free(server_entry->server_info);
2127 silc_atomic_uninit32(&server_entry->internal.deleted);
2128 silc_atomic_uninit32(&server_entry->internal.refcnt);
2129 silc_rwlock_free(server_entry->internal.lock);
2130 silc_free(server_entry);
2131 silc_mutex_unlock(conn->internal->lock);
2135 silc_mutex_unlock(conn->internal->lock);
2136 silc_client_ref_server(client, conn, server_entry);
2138 SILC_LOG_DEBUG(("Added %p", server_entry));
2140 return server_entry;
2143 /* Removes server from the cache by the server entry. */
2145 SilcBool silc_client_del_server(SilcClient client, SilcClientConnection conn,
2146 SilcServerEntry server)
2151 if (silc_atomic_sub_int32(&server->internal.deleted, 1) != 0)
2154 silc_client_unref_server(client, conn, server);
2158 /* Updates the `server_entry' with the new information sent as argument. */
2160 void silc_client_update_server(SilcClient client,
2161 SilcClientConnection conn,
2162 SilcServerEntry server_entry,
2163 const char *server_name,
2164 const char *server_info)
2166 char *server_namec = NULL;
2168 SILC_LOG_DEBUG(("Updating server %p", server_entry));
2171 (!server_entry->server_name ||
2172 !silc_utf8_strcasecmp(server_entry->server_name, server_name))) {
2174 server_namec = silc_identifier_check(server_name, strlen(server_name),
2175 SILC_STRING_UTF8, 256, NULL);
2179 silc_free(server_entry->server_name);
2180 server_entry->server_name = strdup(server_name);
2181 if (!server_entry->server_name)
2184 /* Update cache entry */
2185 silc_mutex_lock(conn->internal->lock);
2186 silc_idcache_update_by_context(conn->internal->server_cache, server_entry,
2187 NULL, server_namec, TRUE);
2188 silc_mutex_unlock(conn->internal->lock);
2192 (!server_entry->server_info ||
2193 !silc_utf8_strcasecmp(server_entry->server_info, server_info))) {
2194 silc_free(server_entry->server_info);
2195 server_entry->server_info = strdup(server_info);
2201 void silc_client_lock_server(SilcServerEntry server_entry)
2203 silc_rwlock_rdlock(server_entry->internal.lock);
2208 void silc_client_unlock_server(SilcServerEntry server_entry)
2210 silc_rwlock_unlock(server_entry->internal.lock);
2213 /* Take reference of server entry */
2215 SilcServerEntry silc_client_ref_server(SilcClient client,
2216 SilcClientConnection conn,
2217 SilcServerEntry server_entry)
2219 silc_atomic_add_int32(&server_entry->internal.refcnt, 1);
2220 SILC_LOG_DEBUG(("Server %p refcnt %d->%d", server_entry,
2221 silc_atomic_get_int32(&server_entry->internal.refcnt) - 1,
2222 silc_atomic_get_int32(&server_entry->internal.refcnt)));
2223 return server_entry;
2226 /* Release reference of server entry */
2228 void silc_client_unref_server(SilcClient client, SilcClientConnection conn,
2229 SilcServerEntry server_entry)
2232 SilcIDCacheEntry id_cache;
2238 if (silc_atomic_sub_int32(&server_entry->internal.refcnt, 1) > 0)
2241 SILC_LOG_DEBUG(("Deleting server %p", server_entry));
2243 silc_mutex_lock(conn->internal->lock);
2244 if (silc_idcache_find_by_context(conn->internal->server_cache, server_entry,
2246 namec = id_cache->name;
2247 ret = silc_idcache_del_by_context(conn->internal->server_cache,
2248 server_entry, NULL);
2251 silc_mutex_unlock(conn->internal->lock);
2253 silc_free(server_entry->server_name);
2254 silc_free(server_entry->server_info);
2255 if (server_entry->public_key)
2256 silc_pkcs_public_key_free(server_entry->public_key);
2257 silc_atomic_uninit32(&server_entry->internal.deleted);
2258 silc_atomic_uninit32(&server_entry->internal.refcnt);
2259 silc_rwlock_free(server_entry->internal.lock);
2260 silc_free(server_entry);
2263 /* Free server entry list */
2265 void silc_client_list_free_servers(SilcClient client,
2266 SilcClientConnection conn,
2267 SilcDList server_list)
2269 SilcServerEntry server_entry;
2272 silc_dlist_start(server_list);
2273 while ((server_entry = silc_dlist_get(server_list)))
2274 silc_client_unref_server(client, conn, server_entry);
2276 silc_dlist_uninit(server_list);