5 Author: Pekka Riikonen <priikone@silcnet.org>
7 Copyright (C) 2001 - 2007 Pekka Riikonen
9 This program is free software; you can redistribute it and/or modify
10 it under the terms of the GNU General Public License as published by
11 the Free Software Foundation; version 2 of the License.
13 This program is distributed in the hope that it will be useful,
14 but WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 GNU General Public License for more details.
22 #include "silcclient.h"
23 #include "client_internal.h"
25 /************************ Client Searching Locally **************************/
27 /* Finds entry for client by the client's ID. Returns the entry or NULL
28 if the entry was not found. */
30 SilcClientEntry silc_client_get_client_by_id(SilcClient client,
31 SilcClientConnection conn,
32 SilcClientID *client_id)
34 SilcIDCacheEntry id_cache;
35 SilcClientEntry client_entry;
37 if (!client || !conn || !client_id)
40 SILC_LOG_DEBUG(("Finding client by ID (%s)",
41 silc_id_render(client_id, SILC_ID_CLIENT)));
43 silc_mutex_lock(conn->internal->lock);
45 /* Find ID from cache */
46 if (!silc_idcache_find_by_id_one(conn->internal->client_cache, client_id,
48 silc_mutex_unlock(conn->internal->lock);
52 client_entry = id_cache->context;
55 silc_client_ref_client(client, conn, client_entry);
56 silc_mutex_unlock(conn->internal->lock);
58 SILC_LOG_DEBUG(("Found"));
63 /* Finds clients by nickname from local cache. */
65 SilcDList silc_client_get_clients_local_ext(SilcClient client,
66 SilcClientConnection conn,
71 SilcIDCacheEntry id_cache;
74 SilcClientEntry entry;
75 char nick[128 + 1], *nicknamec, *parsed = NULL, *format = NULL;
78 if (!client || !conn || !nickname)
81 /* Get nickname from nickname@server string */
82 silc_parse_userfqdn(nickname, nick, sizeof(nick), server, sizeof(server));
84 /* Parse nickname in case it is formatted */
85 if (!silc_client_nickname_parse(client, conn, (char *)nick, &parsed))
88 if (!get_all && parsed)
89 format = (char *)nick;
91 parsed = silc_memdup(nick, strlen(nick));
96 SILC_LOG_DEBUG(("Find clients by nickname %s", parsed));
98 /* Normalize nickname for search */
99 nicknamec = silc_identifier_check(parsed, strlen(parsed),
100 SILC_STRING_UTF8, 128, NULL);
106 clients = silc_dlist_init();
108 silc_free(nicknamec);
113 silc_mutex_lock(conn->internal->lock);
115 /* Find from cache */
116 silc_list_init(list, struct SilcIDCacheEntryStruct, next);
117 if (!silc_idcache_find_by_name(conn->internal->client_cache, nicknamec,
119 silc_mutex_unlock(conn->internal->lock);
120 silc_free(nicknamec);
122 silc_dlist_uninit(clients);
125 silc_list_start(list);
127 if (!format && get_all) {
128 /* Take all without any further checking */
129 while ((id_cache = silc_list_get(list))) {
130 entry = id_cache->context;
131 if (!get_valid || entry->internal.valid) {
132 silc_client_ref_client(client, conn, id_cache->context);
133 silc_dlist_add(clients, id_cache->context);
137 /* Check multiple cache entries for exact match */
138 while ((id_cache = silc_list_get(list))) {
139 entry = id_cache->context;
141 /* If server was provided, find entries that either have no server
142 set or have the same server. Ignore those that have different
144 if (server[0] && entry->server &&
145 !silc_utf8_strcasecmp(entry->server, server))
148 if (silc_utf8_strcasecmp(entry->nickname,
149 format ? format : parsed) &&
150 (!get_valid || entry->internal.valid)) {
151 silc_client_ref_client(client, conn, entry);
152 silc_dlist_add(clients, entry);
154 /* If format is NULL, we find one exact match with the base
155 nickname (parsed). */
162 silc_mutex_unlock(conn->internal->lock);
164 silc_free(nicknamec);
167 if (!silc_dlist_count(clients)) {
168 silc_dlist_uninit(clients);
172 SILC_LOG_DEBUG(("Found %d clients", silc_dlist_count(clients)));
174 silc_dlist_start(clients);
178 /* Finds clients by nickname from local cache. */
180 SilcDList silc_client_get_clients_local(SilcClient client,
181 SilcClientConnection conn,
182 const char *nickname,
185 return silc_client_get_clients_local_ext(client, conn, nickname, return_all,
189 /********************** Client Resolving from Server ************************/
191 /* Resolving context */
194 SilcGetClientCallback completion;
196 SilcClientEntry client_entry;
197 } *SilcClientGetClientInternal;
199 /* Resolving command callback */
201 static SilcBool silc_client_get_clients_cb(SilcClient client,
202 SilcClientConnection conn,
209 SilcClientGetClientInternal i = context;
210 SilcClientEntry client_entry;
212 if (error != SILC_STATUS_OK) {
213 SILC_LOG_DEBUG(("Resolving failed: %s", silc_get_status_message(error)));
215 if (i->client_entry) {
216 i->client_entry->internal.resolve_cmd_ident = 0;
217 silc_client_unref_client(client, conn, i->client_entry);
221 i->completion(client, conn, error, NULL, i->context);
225 /* Add the returned client to list */
227 client_entry = va_arg(ap, SilcClientEntry);
228 silc_client_ref_client(client, conn, client_entry);
229 silc_dlist_add(i->clients, client_entry);
230 client_entry->internal.resolve_cmd_ident = 0;
233 if (status == SILC_STATUS_OK || status == SILC_STATUS_LIST_END) {
234 /* Deliver the clients to the caller */
236 SILC_LOG_DEBUG(("Resolved %d clients", silc_dlist_count(i->clients)));
238 if (i->client_entry) {
239 i->client_entry->internal.resolve_cmd_ident = 0;
240 silc_client_unref_client(client, conn, i->client_entry);
243 silc_dlist_start(i->clients);
244 i->completion(client, conn, SILC_STATUS_OK, i->clients, i->context);
252 silc_client_list_free(client, conn, i->clients);
257 /* Resolves client information from server by the client ID. */
260 silc_client_get_client_by_id_resolve(SilcClient client,
261 SilcClientConnection conn,
262 SilcClientID *client_id,
263 SilcBuffer attributes,
264 SilcGetClientCallback completion,
267 SilcClientGetClientInternal i;
268 SilcClientEntry client_entry;
270 SilcUInt16 cmd_ident;
272 if (!client || !conn | !client_id) {
273 SILC_LOG_ERROR(("Missing arguments to "
274 "silc_client_get_clients_by_id_resolve call"));
278 SILC_LOG_DEBUG(("Resolve client by ID (%s)",
279 silc_id_render(client_id, SILC_ID_CLIENT)));
281 i = silc_calloc(1, sizeof(*i));
284 i->completion = completion;
285 i->context = context;
286 i->clients = silc_dlist_init();
292 /* Attach to resolving, if on going */
293 client_entry = silc_client_get_client_by_id(client, conn, client_id);
294 if (client_entry && client_entry->internal.resolve_cmd_ident) {
295 SILC_LOG_DEBUG(("Attach to existing resolving"));
296 silc_client_unref_client(client, conn, client_entry);
297 silc_client_command_pending(conn, SILC_COMMAND_NONE,
298 client_entry->internal.resolve_cmd_ident,
299 silc_client_get_clients_cb, i);
300 return client_entry->internal.resolve_cmd_ident;
303 /* Send the command */
304 idp = silc_id_payload_encode(client_id, SILC_ID_CLIENT);
305 cmd_ident = silc_client_command_send(client, conn, SILC_COMMAND_WHOIS,
306 silc_client_get_clients_cb, i,
307 2, 3, silc_buffer_datalen(attributes),
308 4, silc_buffer_datalen(idp));
309 if (!cmd_ident && completion)
310 completion(client, conn, SILC_STATUS_ERR_RESOURCE_LIMIT, NULL, context);
312 if (client_entry && cmd_ident) {
313 client_entry->internal.resolve_cmd_ident = cmd_ident;
314 i->client_entry = client_entry;
316 silc_client_unref_client(client, conn, client_entry);
319 silc_buffer_free(idp);
324 /* Finds client entry or entries by the `nickname' and `server'. The
325 completion callback will be called when the client entries has been
326 found. Used internally by the library. */
328 static SilcUInt16 silc_client_get_clients_i(SilcClient client,
329 SilcClientConnection conn,
331 const char *nickname,
333 SilcBuffer attributes,
334 SilcGetClientCallback completion,
337 SilcClientGetClientInternal i;
338 char nick[128 + 1], serv[256 + 1], userhost[768 + 1], *parsed = NULL;
341 SILC_LOG_DEBUG(("Resolve client by %s command",
342 silc_get_command_name(command)));
344 if (!client || !conn) {
345 SILC_LOG_ERROR(("Missing arguments to silc_client_get_clients call"));
348 if (!nickname && !attributes) {
349 SILC_LOG_ERROR(("Missing arguments to silc_client_get_clients call"));
353 /* Parse server name from the nickname if set */
354 if (silc_parse_userfqdn(nickname, nick, sizeof(nick),
355 serv, sizeof(serv)) == 2)
356 server = (const char *)serv;
357 nickname = (const char *)nick;
359 /* Parse nickname in case it is formatted */
360 if (silc_client_nickname_parse(client, conn, (char *)nickname, &parsed))
361 nickname = (const char *)parsed;
363 i = silc_calloc(1, sizeof(*i));
368 i->clients = silc_dlist_init();
374 i->completion = completion;
375 i->context = context;
377 memset(userhost, 0, sizeof(userhost));
378 if (nickname && server) {
379 len = strlen(nickname) + strlen(server) + 3;
380 silc_strncat(userhost, len, nickname, strlen(nickname));
381 silc_strncat(userhost, len, "@", 1);
382 silc_strncat(userhost, len, server, strlen(server));
383 } else if (nickname) {
384 silc_strncat(userhost, sizeof(userhost) - 1, nickname, strlen(nickname));
388 /* Send the command */
389 if (command == SILC_COMMAND_IDENTIFY)
390 return silc_client_command_send(client, conn, SILC_COMMAND_IDENTIFY,
391 silc_client_get_clients_cb, i,
392 1, 1, userhost, strlen(userhost));
393 return silc_client_command_send(client, conn, SILC_COMMAND_WHOIS,
394 silc_client_get_clients_cb, i,
395 2, 1, userhost, strlen(userhost),
396 3, silc_buffer_datalen(attributes));
399 /* Get clients from server with IDENTIFY command */
401 SilcUInt16 silc_client_get_clients(SilcClient client,
402 SilcClientConnection conn,
403 const char *nickname,
405 SilcGetClientCallback completion,
408 return silc_client_get_clients_i(client, conn, SILC_COMMAND_IDENTIFY,
409 nickname, server, NULL,
410 completion, context);
413 /* Get clients from server with WHOIS command */
415 SilcUInt16 silc_client_get_clients_whois(SilcClient client,
416 SilcClientConnection conn,
417 const char *nickname,
419 SilcBuffer attributes,
420 SilcGetClientCallback completion,
423 return silc_client_get_clients_i(client, conn, SILC_COMMAND_WHOIS,
424 nickname, server, attributes,
425 completion, context);
428 /* ID list resolving context */
430 SilcGetClientCallback completion;
432 SilcBuffer client_id_list;
433 SilcUInt32 list_count;
434 } *GetClientsByListInternal;
436 static SilcBool silc_client_get_clients_list_cb(SilcClient client,
437 SilcClientConnection conn,
444 GetClientsByListInternal i = context;
445 SilcClientEntry client_entry;
451 /* Process the list after all replies have been received */
452 if (status != SILC_STATUS_OK && !SILC_STATUS_IS_ERROR(status) &&
453 status != SILC_STATUS_LIST_END)
456 SILC_LOG_DEBUG(("Resolved all clients"));
458 clients = silc_dlist_init();
460 status = SILC_STATUS_ERR_RESOURCE_LIMIT;
464 for (c = 0; c < i->list_count; c++) {
466 SILC_GET16_MSB(idp_len, i->client_id_list->data + 2);
468 if (!silc_id_payload_parse_id(i->client_id_list->data, idp_len, &id)) {
469 status = SILC_STATUS_ERR_BAD_CLIENT_ID;
473 /* Get client entry */
474 client_entry = silc_client_get_client_by_id(client, conn, &id.u.client_id);
476 silc_dlist_add(clients, client_entry);
478 if (!silc_buffer_pull(i->client_id_list, idp_len)) {
479 status = SILC_STATUS_ERR_BAD_CLIENT_ID;
484 silc_dlist_start(clients);
485 status = SILC_STATUS_OK;
487 i->completion(client, conn, status, clients, i->context);
490 if (status != SILC_STATUS_OK && i->completion)
491 i->completion(client, conn, status, NULL, i->context);
493 silc_client_list_free(client, conn, clients);
494 silc_buffer_free(i->client_id_list);
500 /* Gets client entries by the list of client ID's `client_id_list'. This
501 always resolves those client ID's it does not know yet from the server
502 so this function might take a while. The `client_id_list' is a list
503 of ID Payloads added one after other. JOIN command reply and USERS
504 command reply for example returns this sort of list. The `completion'
505 will be called after the entries are available. */
507 SilcUInt16 silc_client_get_clients_by_list(SilcClient client,
508 SilcClientConnection conn,
509 SilcUInt32 list_count,
510 SilcBuffer client_id_list,
511 SilcGetClientCallback completion,
514 GetClientsByListInternal in;
515 SilcClientEntry entry;
516 unsigned char **res_argv = NULL;
517 SilcUInt32 *res_argv_lens = NULL, *res_argv_types = NULL, res_argc = 0;
518 SilcUInt16 idp_len, cmd_ident;
523 SILC_LOG_DEBUG(("Resolve clients from Client ID list"));
525 if (!client || !conn || !client_id_list)
528 in = silc_calloc(1, sizeof(*in));
531 in->completion = completion;
532 in->context = context;
533 in->list_count = list_count;
534 in->client_id_list = silc_buffer_copy(client_id_list);
535 if (!in->client_id_list)
538 for (i = 0; i < list_count; i++) {
540 SILC_GET16_MSB(idp_len, client_id_list->data + 2);
542 if (!silc_id_payload_parse_id(client_id_list->data, idp_len, &id))
545 /* Check if we have this client cached already. If we don't have the
546 entry or it has incomplete info, then resolve it from the server. */
547 entry = silc_client_get_client_by_id(client, conn, &id.u.client_id);
548 if (!entry || !entry->nickname[0] || !entry->username[0] ||
551 res_argv = silc_calloc(list_count, sizeof(*res_argv));
552 res_argv_lens = silc_calloc(list_count, sizeof(*res_argv_lens));
553 res_argv_types = silc_calloc(list_count, sizeof(*res_argv_types));
554 if (!res_argv || !res_argv_lens || !res_argv_types) {
555 silc_client_unref_client(client, conn, entry);
560 res_argv[res_argc] = client_id_list->data;
561 res_argv_lens[res_argc] = idp_len;
562 res_argv_types[res_argc] = res_argc + 4;
565 silc_client_unref_client(client, conn, entry);
567 if (!silc_buffer_pull(client_id_list, idp_len))
570 silc_buffer_start(client_id_list);
572 /* Query the unknown client information from server */
574 cmd_ident = silc_client_command_send_argv(client,
575 conn, SILC_COMMAND_WHOIS,
576 silc_client_get_clients_list_cb,
577 in, res_argc, res_argv,
581 silc_free(res_argv_lens);
582 silc_free(res_argv_types);
586 /* We have the clients in cache, get them and call the completion */
587 silc_client_get_clients_list_cb(client, conn, SILC_COMMAND_WHOIS,
588 SILC_STATUS_OK, SILC_STATUS_OK, in, tmp);
592 silc_buffer_free(in->client_id_list);
595 silc_free(res_argv_lens);
596 silc_free(res_argv_types);
603 SilcClientConnection conn;
604 SilcChannelID channel_id;
605 SilcGetClientCallback completion;
608 } *GetClientsByChannelInternal;
610 SILC_CLIENT_CMD_FUNC(get_clients_by_channel_cb)
612 GetClientsByChannelInternal i = context;
613 SilcClientEntry *clients = NULL;
614 SilcUInt32 clients_count = 0;
615 SilcBool found = FALSE;
616 SilcChannelEntry channel;
617 SilcHashTableList htl;
626 channel = silc_client_get_channel_by_id(i->client, i->conn, &i->channel_id);
627 if (channel && !silc_hash_table_count(channel->user_list)) {
628 clients = silc_calloc(silc_hash_table_count(channel->user_list),
630 silc_hash_table_list(channel->user_list, &htl);
631 while (silc_hash_table_get(&htl, NULL, (void *)&chu))
632 clients[clients_count++] = chu->client;
633 silc_hash_table_list_reset(&htl);
638 i->completion(i->client, i->conn, clients, clients_count, i->context);
641 i->completion(i->client, i->conn, NULL, 0, i->context);
647 /* Gets client entries by the channel entry indicated by `channel'. Thus,
648 it resolves the clients currently on that channel. */
650 void silc_client_get_clients_by_channel(SilcClient client,
651 SilcClientConnection conn,
652 SilcChannelEntry channel,
653 SilcGetClientCallback completion,
656 GetClientsByChannelInternal in;
657 SilcHashTableList htl;
659 SilcClientEntry entry;
660 unsigned char **res_argv = NULL;
661 SilcUInt32 *res_argv_lens = NULL, *res_argv_types = NULL, res_argc = 0;
663 SilcBool wait_res = FALSE;
665 assert(client && conn && channel);
667 SILC_LOG_DEBUG(("Start"));
669 in = silc_calloc(1, sizeof(*in));
672 in->channel_id = *channel->id;
673 in->completion = completion;
674 in->context = context;
676 /* If user list does not exist, send USERS command. */
677 if (!channel->user_list || !silc_hash_table_count(channel->user_list)) {
678 SILC_LOG_DEBUG(("Sending USERS"));
679 silc_client_command_register(client, SILC_COMMAND_USERS, NULL, NULL,
680 silc_client_command_reply_users_i, 0,
682 silc_client_command_send(client, conn, SILC_COMMAND_USERS,
683 conn->cmd_ident, 1, 2, channel->channel_name,
684 strlen(channel->channel_name));
685 silc_client_command_pending(conn, SILC_COMMAND_USERS, conn->cmd_ident,
686 silc_client_command_get_clients_by_channel_cb,
691 silc_hash_table_list(channel->user_list, &htl);
692 while (silc_hash_table_get(&htl, NULL, (void *)&chu)) {
695 /* If the entry has incomplete info, then resolve it from the server. */
696 if (!entry->nickname[0] || !entry->realname) {
697 if (entry->status & SILC_CLIENT_STATUS_RESOLVING) {
698 /* Attach to this resolving and wait until it finishes */
699 silc_client_command_pending(
700 conn, SILC_COMMAND_NONE,
701 entry->resolve_cmd_ident,
702 silc_client_command_get_clients_by_channel_cb,
708 entry->status |= SILC_CLIENT_STATUS_RESOLVING;
709 entry->resolve_cmd_ident = conn->cmd_ident + 1;
711 idp = silc_id_payload_encode(entry->id, SILC_ID_CLIENT);
713 /* No we don't have it, query it from the server. Assemble argument
714 table that will be sent for the WHOIS command later. */
715 res_argv = silc_realloc(res_argv, sizeof(*res_argv) *
717 res_argv_lens = silc_realloc(res_argv_lens, sizeof(*res_argv_lens) *
719 res_argv_types = silc_realloc(res_argv_types, sizeof(*res_argv_types) *
721 res_argv[res_argc] = silc_memdup(idp->data, idp->len);
722 res_argv_lens[res_argc] = idp->len;
723 res_argv_types[res_argc] = res_argc + 4;
726 silc_buffer_free(idp);
729 silc_hash_table_list_reset(&htl);
731 /* Query the client information from server if the list included clients
732 that we don't know about. */
736 /* Send the WHOIS command to server */
737 res_cmd = silc_command_payload_encode(SILC_COMMAND_WHOIS,
738 res_argc, res_argv, res_argv_lens,
739 res_argv_types, ++conn->cmd_ident);
740 silc_client_packet_send(client, conn->sock, SILC_PACKET_COMMAND,
741 NULL, 0, NULL, NULL, res_cmd->data, res_cmd->len,
744 /* Register our own command reply for this command */
745 silc_client_command_register(client, SILC_COMMAND_WHOIS, NULL, NULL,
746 silc_client_command_reply_whois_i, 0,
749 /* Process the applications request after reply has been received */
750 silc_client_command_pending(
751 conn, SILC_COMMAND_WHOIS, conn->cmd_ident,
752 silc_client_command_get_clients_by_channel_cb,
756 silc_buffer_free(res_cmd);
758 silc_free(res_argv_lens);
759 silc_free(res_argv_types);
766 /* We have the clients in cache, get them and call the completion */
767 silc_client_command_get_clients_by_channel_cb((void *)in, NULL);
772 /************************** Client Entry Routines ***************************/
774 /* Creates new client entry and adds it to the ID cache. Returns pointer
777 SilcClientEntry silc_client_add_client(SilcClient client,
778 SilcClientConnection conn,
779 char *nickname, char *username,
780 char *userinfo, SilcClientID *id,
783 SilcClientEntry client_entry;
784 char *nick = NULL, parsed[128 + 1];
786 SILC_LOG_DEBUG(("Adding new client entry"));
788 /* Save the client infos */
789 client_entry = silc_calloc(1, sizeof(*client_entry));
793 silc_rwlock_alloc(&client_entry->internal.lock);
794 silc_atomic_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_free(client_entry);
822 /* Normalize nickname */
823 if (client_entry->nickname[0]) {
824 nick = silc_identifier_check(parsed, strlen(parsed),
825 SILC_STRING_UTF8, 128, NULL);
827 silc_free(client_entry->realname);
828 silc_hash_table_free(client_entry->channels);
829 silc_free(client_entry);
834 silc_mutex_lock(conn->internal->lock);
836 /* Add client to cache, the normalized nickname is saved to cache */
837 if (!silc_idcache_add(conn->internal->client_cache, nick,
838 &client_entry->id, client_entry)) {
840 silc_free(client_entry->realname);
841 silc_hash_table_free(client_entry->channels);
842 silc_free(client_entry);
843 silc_mutex_unlock(conn->internal->lock);
847 client_entry->nickname_normalized = nick;
849 silc_mutex_unlock(conn->internal->lock);
850 silc_client_ref_client(client, conn, client_entry);
852 /* Format the nickname */
853 silc_client_nickname_format(client, conn, client_entry, FALSE);
855 if (client_entry->nickname[0])
856 client_entry->internal.valid = TRUE;
858 SILC_LOG_DEBUG(("Added %p", client_entry));
863 /* Updates the `client_entry' with the new information sent as argument.
864 This handles entry locking internally. */
866 void silc_client_update_client(SilcClient client,
867 SilcClientConnection conn,
868 SilcClientEntry client_entry,
869 const char *nickname,
870 const char *username,
871 const char *userinfo,
874 char *nick = NULL, parsed[128 + 1];
876 SILC_LOG_DEBUG(("Update client entry"));
878 silc_rwlock_wrlock(client_entry->internal.lock);
880 if (!client_entry->realname && userinfo)
881 client_entry->realname = strdup(userinfo);
883 if ((!client_entry->username[0] || !client_entry->hostname[0]) && username)
884 silc_parse_userfqdn(username, client_entry->username,
885 sizeof(client_entry->username),
886 client_entry->hostname,
887 sizeof(client_entry->username));
889 if (!client_entry->nickname[0] && nickname) {
890 silc_parse_userfqdn(nickname, parsed, sizeof(parsed),
891 client_entry->server, sizeof(client_entry->server));
892 if (client->internal->params->full_nicknames)
893 silc_snprintf(client_entry->nickname, sizeof(client_entry->nickname),
896 silc_snprintf(client_entry->nickname, sizeof(client_entry->nickname),
899 /* Normalize nickname */
900 nick = silc_identifier_check(parsed, strlen(parsed),
901 SILC_STRING_UTF8, 128, NULL);
903 silc_rwlock_unlock(client_entry->internal.lock);
907 /* Format nickname */
908 silc_client_nickname_format(client, conn, client_entry,
909 client_entry == conn->local_entry);
911 /* Update cache entry */
912 silc_mutex_lock(conn->internal->lock);
913 silc_idcache_update_by_context(conn->internal->client_cache,
914 client_entry, NULL, nick, TRUE);
915 silc_mutex_unlock(conn->internal->lock);
916 client_entry->nickname_normalized = nick;
917 client_entry->internal.valid = TRUE;
919 client_entry->mode = mode;
921 silc_rwlock_unlock(client_entry->internal.lock);
924 /* Change a client's nickname. Must be called with `client_entry' locked. */
926 SilcBool silc_client_change_nickname(SilcClient client,
927 SilcClientConnection conn,
928 SilcClientEntry client_entry,
929 const char *new_nick,
930 SilcClientID *new_id,
931 const unsigned char *idp,
936 SILC_LOG_DEBUG(("Change nickname %s to %s", client_entry->nickname,
939 /* Normalize nickname */
940 tmp = silc_identifier_check(new_nick, strlen(new_nick),
941 SILC_STRING_UTF8, 128, NULL);
945 /* Update the client entry */
946 silc_mutex_lock(conn->internal->lock);
947 if (!silc_idcache_update_by_context(conn->internal->client_cache,
948 client_entry, new_id, tmp, TRUE)) {
950 silc_mutex_unlock(conn->internal->lock);
953 silc_mutex_unlock(conn->internal->lock);
955 memset(client_entry->nickname, 0, sizeof(client_entry->nickname));
956 memcpy(client_entry->nickname, new_nick, strlen(new_nick));
957 client_entry->nickname_normalized = tmp;
958 silc_client_nickname_format(client, conn, client_entry,
959 client_entry == conn->local_entry);
961 /* For my client entry, update ID and set new ID to packet stream */
962 if (client_entry == conn->local_entry) {
963 if (idp && idp_len) {
964 silc_buffer_enlarge(conn->internal->local_idp, idp_len);
965 silc_buffer_put(conn->internal->local_idp, idp, idp_len);
968 silc_packet_set_ids(conn->stream, SILC_ID_CLIENT, conn->local_id,
972 client_entry->internal.valid = TRUE;
976 /* Deletes the client entry and frees all memory. */
978 void silc_client_del_client_entry(SilcClient client,
979 SilcClientConnection conn,
980 SilcClientEntry client_entry)
982 silc_free(client_entry->realname);
983 silc_free(client_entry->nickname_normalized);
984 silc_free(client_entry->internal.key);
985 if (client_entry->public_key)
986 silc_pkcs_public_key_free(client_entry->public_key);
987 silc_hash_table_free(client_entry->channels);
988 if (client_entry->internal.send_key)
989 silc_cipher_free(client_entry->internal.send_key);
990 if (client_entry->internal.receive_key)
991 silc_cipher_free(client_entry->internal.receive_key);
992 if (client_entry->internal.hmac_send)
993 silc_hmac_free(client_entry->internal.hmac_send);
994 if (client_entry->internal.hmac_receive)
995 silc_hmac_free(client_entry->internal.hmac_receive);
996 silc_client_ftp_session_free_client(client, client_entry);
997 if (client_entry->internal.ke)
998 silc_client_abort_key_agreement(client, conn, client_entry);
999 silc_atomic_uninit32(&client_entry->internal.deleted);
1000 silc_atomic_uninit32(&client_entry->internal.refcnt);
1001 silc_rwlock_free(client_entry->internal.lock);
1002 silc_free(client_entry);
1005 /* Removes client from the cache by the client entry. */
1007 SilcBool silc_client_del_client(SilcClient client, SilcClientConnection conn,
1008 SilcClientEntry client_entry)
1013 SILC_LOG_DEBUG(("Marking client entry %p deleted"));
1015 if (silc_atomic_sub_int32(&client_entry->internal.deleted, 1) != 0) {
1016 SILC_LOG_DEBUG(("Client entry %p already marked deleted"));
1020 silc_client_unref_client(client, conn, client_entry);
1024 /* Internal routine used to find client by ID and if not found this creates
1025 new client entry and returns it. */
1027 SilcClientEntry silc_client_get_client(SilcClient client,
1028 SilcClientConnection conn,
1029 SilcClientID *client_id)
1031 SilcClientEntry client_entry;
1033 client_entry = silc_client_get_client_by_id(client, conn, client_id);
1034 if (!client_entry) {
1035 client_entry = silc_client_add_client(client, conn, NULL, NULL, NULL,
1039 silc_client_ref_client(client, conn, client_entry);
1042 return client_entry;
1047 void silc_client_lock_client(SilcClientEntry client_entry)
1049 silc_rwlock_rdlock(client_entry->internal.lock);
1054 void silc_client_unlock_client(SilcClientEntry client_entry)
1056 silc_rwlock_unlock(client_entry->internal.lock);
1059 /* Take reference of client entry */
1061 SilcClientEntry silc_client_ref_client(SilcClient client,
1062 SilcClientConnection conn,
1063 SilcClientEntry client_entry)
1065 silc_atomic_add_int32(&client_entry->internal.refcnt, 1);
1066 SILC_LOG_DEBUG(("Client %p refcnt %d->%d", client_entry,
1067 silc_atomic_get_int32(&client_entry->internal.refcnt) - 1,
1068 silc_atomic_get_int32(&client_entry->internal.refcnt)));
1069 return client_entry;
1072 /* Release reference of client entry */
1074 void silc_client_unref_client(SilcClient client, SilcClientConnection conn,
1075 SilcClientEntry client_entry)
1082 SILC_LOG_DEBUG(("Client %p refcnt %d->%d", client_entry,
1083 silc_atomic_get_int32(&client_entry->internal.refcnt),
1084 silc_atomic_get_int32(&client_entry->internal.refcnt) - 1));
1086 if (silc_atomic_sub_int32(&client_entry->internal.refcnt, 1) > 0)
1089 SILC_LOG_DEBUG(("Deleting client %p (%d)", client_entry,
1090 silc_atomic_get_int32(&client_entry->internal.deleted)));
1092 silc_mutex_lock(conn->internal->lock);
1093 ret = silc_idcache_del_by_context(conn->internal->client_cache,
1094 client_entry, NULL);
1095 silc_mutex_unlock(conn->internal->lock);
1098 /* Remove from channels */
1099 silc_client_remove_from_channels(client, conn, client_entry);
1101 /* Free the client entry data */
1102 silc_client_del_client_entry(client, conn, client_entry);
1106 /* Free client entry list */
1108 void silc_client_list_free(SilcClient client, SilcClientConnection conn,
1109 SilcDList client_list)
1111 SilcClientEntry client_entry;
1114 silc_dlist_start(client_list);
1115 while ((client_entry = silc_dlist_get(client_list)))
1116 silc_client_unref_client(client, conn, client_entry);
1118 silc_dlist_uninit(client_list);
1122 /* Formats the nickname of the client specified by the `client_entry'.
1123 If the format is specified by the application this will format the
1124 nickname and replace the old nickname in the client entry. If the
1125 format string is not specified then this function has no effect.
1126 Returns the client entry that was formatted. */
1128 SilcClientEntry silc_client_nickname_format(SilcClient client,
1129 SilcClientConnection conn,
1130 SilcClientEntry client_entry,
1134 char newnick[128 + 1];
1135 int i, off = 0, len;
1137 SilcClientEntry entry, unformatted = NULL;
1138 SilcBool formatted = FALSE;
1140 if (!client->internal->params->nickname_format[0])
1141 return client_entry;
1142 if (!client_entry->nickname[0])
1145 SILC_LOG_DEBUG(("Format nickname"));
1147 /* Get all clients with same nickname. Do not perform the formatting
1148 if there aren't any clients with same nickname unless the application
1149 is forcing us to do so. */
1150 clients = silc_client_get_clients_local_ext(client, conn,
1151 client_entry->nickname,
1155 if (silc_dlist_count(clients) == 1 && !priority &&
1156 !client->internal->params->nickname_force_format) {
1157 silc_client_list_free(client, conn, clients);
1158 return client_entry;
1161 /* Is the requested client formatted already */
1162 if (client_entry->nickname_normalized &&
1163 !silc_utf8_strcasecmp(client_entry->nickname,
1164 client_entry->nickname_normalized))
1167 if (client->internal->params->nickname_force_format)
1170 /* Find unformatted client entry */
1171 while ((entry = silc_dlist_get(clients))) {
1172 if (!entry->internal.valid)
1174 if (entry == client_entry)
1176 if (silc_utf8_strcasecmp(entry->nickname, entry->nickname_normalized)) {
1177 unformatted = entry;
1182 /* If there are no other unformatted clients and the requested client is
1183 unformatted, just return it. */
1184 if (!unformatted && !formatted) {
1185 silc_client_list_free(client, conn, clients);
1186 return client_entry;
1189 /* If priority formatting then the requested client will get the
1190 unformatted nickname, and the unformatted client will get a new
1191 formatted nickname. */
1194 /* Simply change the client's nickname to unformatted */
1195 if (!silc_client_nickname_parse(client, conn, client_entry->nickname,
1199 silc_snprintf(client_entry->nickname, sizeof(client_entry->nickname),
1205 /* There was no other unformatted client */
1206 silc_client_list_free(client, conn, clients);
1207 return client_entry;
1210 /* Now format the previously unformatted client */
1211 client_entry = unformatted;
1215 /* If already formatted just return it */
1217 silc_client_list_free(client, conn, clients);
1218 return client_entry;
1221 memset(newnick, 0, sizeof(newnick));
1222 cp = client->internal->params->nickname_format;
1232 if (!client_entry->nickname[0])
1234 len = strlen(client_entry->nickname);
1235 memcpy(&newnick[off], client_entry->nickname, len);
1239 /* Stripped hostname */
1240 if (!client_entry->hostname[0])
1242 len = strcspn(client_entry->hostname, ".");
1243 i = strcspn(client_entry->hostname, "-");
1246 memcpy(&newnick[off], client_entry->hostname, len);
1251 if (!client_entry->hostname[0])
1253 len = strlen(client_entry->hostname);
1254 memcpy(&newnick[off], client_entry->hostname, len);
1258 /* Ascending number */
1263 if (silc_dlist_count(clients) == 1)
1266 silc_dlist_start(clients);
1267 while ((entry = silc_dlist_get(clients))) {
1268 if (!silc_utf8_strncasecmp(entry->nickname, newnick, off))
1270 if (strlen(entry->nickname) <= off)
1272 num = atoi(&entry->nickname[off]);
1277 memset(tmp, 0, sizeof(tmp));
1278 silc_snprintf(tmp, sizeof(tmp) - 1, "%d", ++max);
1280 memcpy(&newnick[off], tmp, len);
1285 /* Some other character in the string */
1286 memcpy(&newnick[off], cp, 1);
1295 memset(client_entry->nickname, 0, sizeof(client_entry->nickname));
1296 memcpy(client_entry->nickname, newnick, strlen(newnick));
1297 silc_client_list_free(client, conn, clients);
1299 return client_entry;
1302 /* Parses nickname according to nickname format string */
1304 SilcBool silc_client_nickname_parse(SilcClient client,
1305 SilcClientConnection conn,
1309 char *cp, s = 0, e = 0, *nick;
1313 if (!client->internal->params->nickname_format[0]) {
1318 if (!nickname || !nickname[0])
1321 cp = client->internal->params->nickname_format;
1339 /* Get separator character */
1352 /* Parse the nickname */
1356 if (strchr(nickname, s))
1357 nick = strchr(nickname, s) + 1;
1359 if (strchr(nick, e))
1360 len = strchr(nick, e) - nick;
1364 *ret_nick = silc_memdup(nick, len);
1368 SILC_LOG_DEBUG(("Parsed nickname: %s", *ret_nick));
1373 /************************ Channel Searching Locally *************************/
1375 /* Finds entry for channel by the channel name. Returns the entry or NULL
1376 if the entry was not found. It is found only if the client is joined
1379 SilcChannelEntry silc_client_get_channel(SilcClient client,
1380 SilcClientConnection conn,
1384 SilcIDCacheEntry id_cache;
1385 SilcChannelEntry entry = NULL;
1386 char chname[256 + 1], server[256 + 1];
1388 if (!client || !conn || !channel)
1391 SILC_LOG_DEBUG(("Find channel %s", channel));
1393 /* Parse server name from channel name */
1394 silc_parse_userfqdn(channel, chname, sizeof(chname), server, sizeof(server));
1396 /* Normalize name for search */
1397 channel = silc_channel_name_check(chname, strlen(chname), SILC_STRING_UTF8,
1402 silc_mutex_lock(conn->internal->lock);
1404 if (!silc_idcache_find_by_name(conn->internal->channel_cache, channel,
1406 silc_mutex_unlock(conn->internal->lock);
1411 /* If server name was specified with channel name, find the correct
1412 channel entry with the server name. There can only be one channel
1413 with same name on same server. */
1414 silc_list_start(list);
1416 while ((id_cache = silc_list_get(list))) {
1417 entry = id_cache->context;
1418 if (!entry->server[0])
1420 if (silc_utf8_strcasecmp(entry->server, server))
1424 /* Get first channel without server name specified or one with our
1425 current server connection name */
1426 while ((id_cache = silc_list_get(list))) {
1427 entry = id_cache->context;
1428 if (!entry->server[0])
1430 if (silc_utf8_strcasecmp(entry->server, conn->remote_host))
1436 silc_mutex_unlock(conn->internal->lock);
1441 SILC_LOG_DEBUG(("Found channel %s%s%s", entry->channel_name,
1442 entry->server[0] ? "@" : "", entry->server));
1445 silc_client_ref_channel(client, conn, entry);
1446 silc_mutex_unlock(conn->internal->lock);
1453 /* Finds entry for channel by the channel ID. Returns the entry or NULL
1454 if the entry was not found. It is found only if the client is joined
1457 SilcChannelEntry silc_client_get_channel_by_id(SilcClient client,
1458 SilcClientConnection conn,
1459 SilcChannelID *channel_id)
1461 SilcIDCacheEntry id_cache;
1462 SilcChannelEntry entry;
1464 if (!client || !conn || !channel_id)
1467 SILC_LOG_DEBUG(("Find channel by id %s",
1468 silc_id_render(channel_id, SILC_ID_CHANNEL)));
1470 silc_mutex_lock(conn->internal->lock);
1472 if (!silc_idcache_find_by_id_one(conn->internal->channel_cache, channel_id,
1474 silc_mutex_unlock(conn->internal->lock);
1478 SILC_LOG_DEBUG(("Found"));
1480 entry = id_cache->context;
1483 silc_client_ref_channel(client, conn, entry);
1484 silc_mutex_unlock(conn->internal->lock);
1489 /********************** Channel Resolving from Server ***********************/
1491 /* Channel resolving context */
1494 SilcGetChannelCallback completion;
1496 } *SilcClientGetChannelInternal;
1498 /* Resolving command callback */
1500 static SilcBool silc_client_get_channel_cb(SilcClient client,
1501 SilcClientConnection conn,
1502 SilcCommand command,
1508 SilcClientGetChannelInternal i = context;
1509 SilcChannelEntry entry;
1511 if (error != SILC_STATUS_OK) {
1512 SILC_LOG_DEBUG(("Resolving failed: %s", silc_get_status_message(error)));
1514 i->completion(client, conn, error, NULL, i->context);
1518 /* Add the returned channel to list */
1519 if (i->completion) {
1520 entry = va_arg(ap, SilcChannelEntry);
1521 silc_client_ref_channel(client, conn, entry);
1522 silc_dlist_add(i->channels, entry);
1525 if (status == SILC_STATUS_OK || status == SILC_STATUS_LIST_END) {
1526 /* Deliver the channels to the caller */
1527 if (i->completion) {
1528 SILC_LOG_DEBUG(("Resolved %d channels", silc_dlist_count(i->channels)));
1529 silc_dlist_start(i->channels);
1530 i->completion(client, conn, SILC_STATUS_OK, i->channels, i->context);
1538 silc_client_list_free_channels(client, conn, i->channels);
1543 /* Resolves channel entry from the server by the channel name. */
1545 void silc_client_get_channel_resolve(SilcClient client,
1546 SilcClientConnection conn,
1548 SilcGetChannelCallback completion,
1551 SilcClientGetChannelInternal i;
1553 if (!client || !conn || !channel_name || !completion)
1556 SILC_LOG_DEBUG(("Resolve channel %s", channel_name));
1558 i = silc_calloc(1, sizeof(*i));
1561 i->completion = completion;
1562 i->context = context;
1563 i->channels = silc_dlist_init();
1569 /* Send the command */
1570 if (!silc_client_command_send(client, conn, SILC_COMMAND_IDENTIFY,
1571 silc_client_get_channel_cb, i, 1,
1572 3, channel_name, strlen(channel_name))) {
1574 completion(client, conn, SILC_STATUS_ERR_RESOURCE_LIMIT, NULL, context);
1578 /* Resolves channel information from the server by the channel ID. */
1581 silc_client_get_channel_by_id_resolve(SilcClient client,
1582 SilcClientConnection conn,
1583 SilcChannelID *channel_id,
1584 SilcGetChannelCallback completion,
1587 SilcClientGetChannelInternal i;
1589 SilcUInt16 cmd_ident;
1591 if (!client || !conn || !channel_id || !completion)
1594 SILC_LOG_DEBUG(("Resolve channel by id %s",
1595 silc_id_render(channel_id, SILC_ID_CHANNEL)));
1597 i = silc_calloc(1, sizeof(*i));
1600 i->completion = completion;
1601 i->context = context;
1602 i->channels = silc_dlist_init();
1608 /* Send the command */
1609 idp = silc_id_payload_encode(channel_id, SILC_ID_CHANNEL);
1610 cmd_ident = silc_client_command_send(client, conn, SILC_COMMAND_IDENTIFY,
1611 silc_client_get_channel_cb, i, 1,
1612 5, silc_buffer_datalen(idp));
1613 silc_buffer_free(idp);
1614 if (!cmd_ident && completion)
1615 completion(client, conn, SILC_STATUS_ERR_RESOURCE_LIMIT, NULL, context);
1620 /************************* Channel Entry Routines ***************************/
1622 /* Add new channel entry to the ID Cache */
1624 SilcChannelEntry silc_client_add_channel(SilcClient client,
1625 SilcClientConnection conn,
1626 const char *channel_name,
1628 SilcChannelID *channel_id)
1630 SilcChannelEntry channel;
1631 char *channel_namec, name[256 + 1];
1633 SILC_LOG_DEBUG(("Adding channel %s", channel_name));
1635 channel = silc_calloc(1, sizeof(*channel));
1639 silc_rwlock_alloc(&channel->internal.lock);
1640 silc_atomic_init32(&channel->internal.refcnt, 0);
1641 silc_atomic_init32(&channel->internal.deleted, 1);
1642 channel->id = *channel_id;
1643 channel->mode = mode;
1645 silc_parse_userfqdn(channel_name, name, sizeof(name),
1646 channel->server, sizeof(channel->server));
1647 if (client->internal->params->full_channel_names)
1648 channel->channel_name = strdup(channel_name);
1650 channel->channel_name = strdup(name);
1652 if (!channel->channel_name) {
1653 silc_rwlock_free(channel->internal.lock);
1654 silc_atomic_uninit32(&channel->internal.refcnt);
1659 channel->user_list = silc_hash_table_alloc(1, silc_hash_ptr, NULL, NULL,
1660 NULL, NULL, NULL, TRUE);
1661 if (!channel->user_list) {
1662 silc_rwlock_free(channel->internal.lock);
1663 silc_atomic_uninit32(&channel->internal.refcnt);
1664 silc_free(channel->channel_name);
1669 /* Normalize channel name */
1670 channel_namec = silc_channel_name_check(name, strlen(name),
1671 SILC_STRING_UTF8, 256, NULL);
1672 if (!channel_namec) {
1673 silc_rwlock_free(channel->internal.lock);
1674 silc_atomic_uninit32(&channel->internal.refcnt);
1675 silc_free(channel->channel_name);
1676 silc_hash_table_free(channel->user_list);
1681 silc_mutex_lock(conn->internal->lock);
1683 /* Add channel to cache, the normalized channel name is saved to cache */
1684 if (!silc_idcache_add(conn->internal->channel_cache, channel_namec,
1685 &channel->id, channel)) {
1686 silc_rwlock_free(channel->internal.lock);
1687 silc_atomic_uninit32(&channel->internal.refcnt);
1688 silc_free(channel_namec);
1689 silc_free(channel->channel_name);
1690 silc_hash_table_free(channel->user_list);
1692 silc_mutex_unlock(conn->internal->lock);
1696 silc_mutex_unlock(conn->internal->lock);
1697 silc_client_ref_channel(client, conn, channel);
1699 SILC_LOG_DEBUG(("Added %p", channel));
1704 /* Removes channel from the cache by the channel entry. */
1706 SilcBool silc_client_del_channel(SilcClient client, SilcClientConnection conn,
1707 SilcChannelEntry channel)
1712 SILC_LOG_DEBUG(("Marking channel entry %p deleted"));
1714 if (silc_atomic_sub_int32(&channel->internal.deleted, 1) != 0) {
1715 SILC_LOG_DEBUG(("Channel entry %p already marked deleted"));
1719 silc_client_unref_channel(client, conn, channel);
1723 /* Replaces the channel ID of the `channel' to `new_id'. Returns FALSE
1724 if the ID could not be changed. This handles entry locking internally. */
1726 SilcBool silc_client_replace_channel_id(SilcClient client,
1727 SilcClientConnection conn,
1728 SilcChannelEntry channel,
1729 SilcChannelID *new_id)
1731 SilcBool ret = FALSE;
1736 SILC_LOG_DEBUG(("Old Channel ID id(%s)",
1737 silc_id_render(&channel->id, SILC_ID_CHANNEL)));
1738 SILC_LOG_DEBUG(("New Channel ID id(%s)",
1739 silc_id_render(new_id, SILC_ID_CHANNEL)));
1742 silc_rwlock_wrlock(channel->internal.lock);
1743 silc_mutex_lock(conn->internal->lock);
1744 silc_idcache_update_by_context(conn->internal->channel_cache, channel,
1745 new_id, NULL, FALSE);
1746 silc_mutex_unlock(conn->internal->lock);
1747 silc_rwlock_unlock(channel->internal.lock);
1754 void silc_client_lock_channel(SilcChannelEntry channel_entry)
1756 silc_rwlock_rdlock(channel_entry->internal.lock);
1759 /* Unlock channel */
1761 void silc_client_unlock_channel(SilcChannelEntry channel_entry)
1763 silc_rwlock_unlock(channel_entry->internal.lock);
1766 /* Take reference of channel entry */
1768 SilcChannelEntry silc_client_ref_channel(SilcClient client,
1769 SilcClientConnection conn,
1770 SilcChannelEntry channel_entry)
1772 silc_atomic_add_int32(&channel_entry->internal.refcnt, 1);
1773 SILC_LOG_DEBUG(("Channel %p refcnt %d->%d", channel_entry,
1774 silc_atomic_get_int32(&channel_entry->internal.refcnt) - 1,
1775 silc_atomic_get_int32(&channel_entry->internal.refcnt)));
1776 return channel_entry;
1779 /* Release reference of channel entry */
1781 void silc_client_unref_channel(SilcClient client, SilcClientConnection conn,
1782 SilcChannelEntry channel_entry)
1784 SilcIDCacheEntry id_cache;
1785 SilcBool ret = TRUE;
1793 SILC_LOG_DEBUG(("Channel %p refcnt %d->%d", channel_entry,
1794 silc_atomic_get_int32(&channel_entry->internal.refcnt),
1795 silc_atomic_get_int32(&channel_entry->internal.refcnt)
1798 if (silc_atomic_sub_int32(&channel_entry->internal.refcnt, 1) > 0)
1801 SILC_LOG_DEBUG(("Deleting channel %p", channel_entry));
1803 silc_mutex_lock(conn->internal->lock);
1804 if (silc_idcache_find_by_context(conn->internal->channel_cache, channel_entry,
1806 namec = id_cache->name;
1807 ret = silc_idcache_del_by_context(conn->internal->channel_cache,
1808 channel_entry, NULL);
1811 silc_mutex_unlock(conn->internal->lock);
1816 silc_client_empty_channel(client, conn, channel_entry);
1817 silc_client_del_channel_private_keys(client, conn, channel_entry);
1818 silc_hash_table_free(channel_entry->user_list);
1819 silc_free(channel_entry->channel_name);
1820 silc_free(channel_entry->topic);
1821 if (channel_entry->founder_key)
1822 silc_pkcs_public_key_free(channel_entry->founder_key);
1823 if (channel_entry->internal.send_key)
1824 silc_cipher_free(channel_entry->internal.send_key);
1825 if (channel_entry->internal.receive_key)
1826 silc_cipher_free(channel_entry->internal.receive_key);
1827 if (channel_entry->internal.hmac)
1828 silc_hmac_free(channel_entry->internal.hmac);
1829 if (channel_entry->internal.old_channel_keys) {
1830 silc_dlist_start(channel_entry->internal.old_channel_keys);
1831 while ((key = silc_dlist_get(channel_entry->internal.old_channel_keys)))
1832 silc_cipher_free(key);
1833 silc_dlist_uninit(channel_entry->internal.old_channel_keys);
1835 if (channel_entry->internal.old_hmacs) {
1836 silc_dlist_start(channel_entry->internal.old_hmacs);
1837 while ((hmac = silc_dlist_get(channel_entry->internal.old_hmacs)))
1838 silc_hmac_free(hmac);
1839 silc_dlist_uninit(channel_entry->internal.old_hmacs);
1841 if (channel_entry->channel_pubkeys)
1842 silc_argument_list_free(channel_entry->channel_pubkeys,
1843 SILC_ARGUMENT_PUBLIC_KEY);
1844 silc_atomic_uninit32(&channel_entry->internal.deleted);
1845 silc_atomic_uninit32(&channel_entry->internal.refcnt);
1846 silc_rwlock_free(channel_entry->internal.lock);
1847 silc_schedule_task_del_by_context(conn->client->schedule, channel_entry);
1848 silc_free(channel_entry);
1851 /* Free channel entry list */
1853 void silc_client_list_free_channels(SilcClient client,
1854 SilcClientConnection conn,
1855 SilcDList channel_list)
1857 SilcChannelEntry channel_entry;
1860 silc_dlist_start(channel_list);
1861 while ((channel_entry = silc_dlist_get(channel_list)))
1862 silc_client_unref_channel(client, conn, channel_entry);
1864 silc_dlist_uninit(channel_list);
1868 /************************* Server Searching Locally *************************/
1870 /* Finds entry for server by the server name. */
1872 SilcServerEntry silc_client_get_server(SilcClient client,
1873 SilcClientConnection conn,
1876 SilcIDCacheEntry id_cache;
1877 SilcServerEntry entry;
1879 if (!client || !conn || !server_name)
1882 SILC_LOG_DEBUG(("Find server by name %s", server_name));
1884 /* Normalize server name for search */
1885 server_name = silc_identifier_check(server_name, strlen(server_name),
1886 SILC_STRING_UTF8, 256, NULL);
1890 silc_mutex_lock(conn->internal->lock);
1892 if (!silc_idcache_find_by_name_one(conn->internal->server_cache,
1893 server_name, &id_cache)) {
1894 silc_free(server_name);
1895 silc_mutex_unlock(conn->internal->lock);
1899 SILC_LOG_DEBUG(("Found"));
1902 entry = id_cache->context;
1903 silc_client_ref_server(client, conn, entry);
1905 silc_mutex_unlock(conn->internal->lock);
1907 silc_free(server_name);
1912 /* Finds entry for server by the server ID. */
1914 SilcServerEntry silc_client_get_server_by_id(SilcClient client,
1915 SilcClientConnection conn,
1916 SilcServerID *server_id)
1918 SilcIDCacheEntry id_cache;
1919 SilcServerEntry entry;
1921 if (!client || !conn || !server_id)
1924 SILC_LOG_DEBUG(("Find server by id %s",
1925 silc_id_render(server_id, SILC_ID_SERVER)));
1927 silc_mutex_lock(conn->internal->lock);
1929 if (!silc_idcache_find_by_id_one(conn->internal->server_cache,
1930 server_id, &id_cache)) {
1931 silc_mutex_unlock(conn->internal->lock);
1935 SILC_LOG_DEBUG(("Found"));
1938 entry = id_cache->context;
1939 silc_client_ref_server(client, conn, entry);
1941 silc_mutex_unlock(conn->internal->lock);
1946 /*********************** Server Resolving from Server ***********************/
1948 /* Resolving context */
1951 SilcGetServerCallback completion;
1953 } *SilcClientGetServerInternal;
1955 /* Resolving command callback */
1957 static SilcBool silc_client_get_server_cb(SilcClient client,
1958 SilcClientConnection conn,
1959 SilcCommand command,
1965 SilcClientGetServerInternal i = context;
1966 SilcServerEntry server;
1968 if (error != SILC_STATUS_OK) {
1969 SILC_LOG_DEBUG(("Resolving failed: %s", silc_get_status_message(error)));
1971 i->completion(client, conn, error, NULL, i->context);
1975 /* Add the returned servers to list */
1976 if (i->completion) {
1977 server = va_arg(ap, SilcServerEntry);
1978 silc_client_ref_server(client, conn, server);
1979 silc_dlist_add(i->servers, server);
1980 server->internal.resolve_cmd_ident = 0;
1983 if (status == SILC_STATUS_OK || status == SILC_STATUS_LIST_END) {
1984 /* Deliver the servers to the caller */
1985 if (i->completion) {
1986 SILC_LOG_DEBUG(("Resolved %d servers", silc_dlist_count(i->servers)));
1987 silc_dlist_start(i->servers);
1988 i->completion(client, conn, SILC_STATUS_OK, i->servers, i->context);
1996 silc_client_list_free_servers(client, conn, i->servers);
2001 /* Resolve server by server ID */
2004 silc_client_get_server_by_id_resolve(SilcClient client,
2005 SilcClientConnection conn,
2006 SilcServerID *server_id,
2007 SilcGetServerCallback completion,
2010 SilcClientGetServerInternal i;
2011 SilcServerEntry server;
2013 SilcUInt16 cmd_ident;
2015 if (!client || !conn || !server_id || !completion)
2018 SILC_LOG_DEBUG(("Resolve server by id %s",
2019 silc_id_render(server_id, SILC_ID_SERVER)));
2021 i = silc_calloc(1, sizeof(*i));
2024 i->completion = completion;
2025 i->context = context;
2026 i->servers = silc_dlist_init();
2032 /* Attach to resolving, if on going */
2033 server = silc_client_get_server_by_id(client, conn, server_id);
2034 if (server && server->internal.resolve_cmd_ident) {
2035 SILC_LOG_DEBUG(("Attach to existing resolving"));
2036 silc_client_unref_server(client, conn, server);
2037 silc_client_command_pending(conn, SILC_COMMAND_NONE,
2038 server->internal.resolve_cmd_ident,
2039 silc_client_get_server_cb, i);
2040 return server->internal.resolve_cmd_ident;
2043 /* Send the command */
2044 idp = silc_id_payload_encode(server_id, SILC_ID_SERVER);
2045 cmd_ident = silc_client_command_send(client, conn, SILC_COMMAND_IDENTIFY,
2046 silc_client_get_server_cb, i, 1,
2047 5, silc_buffer_datalen(idp));
2048 silc_buffer_free(idp);
2049 if (!cmd_ident && completion)
2050 completion(client, conn, SILC_STATUS_ERR_RESOURCE_LIMIT, NULL, context);
2052 if (server && cmd_ident)
2053 server->internal.resolve_cmd_ident = cmd_ident;
2055 silc_client_unref_server(client, conn, server);
2060 /************************** Server Entry Routines ***************************/
2062 /* Add new server entry */
2064 SilcServerEntry silc_client_add_server(SilcClient client,
2065 SilcClientConnection conn,
2066 const char *server_name,
2067 const char *server_info,
2068 SilcServerID *server_id)
2070 SilcServerEntry server_entry;
2071 char *server_namec = NULL;
2076 SILC_LOG_DEBUG(("Adding new server %s", server_name));
2078 server_entry = silc_calloc(1, sizeof(*server_entry));
2082 silc_rwlock_alloc(&server_entry->internal.lock);
2083 silc_atomic_init32(&server_entry->internal.refcnt, 0);
2084 server_entry->id = *server_id;
2086 server_entry->server_name = strdup(server_name);
2088 server_entry->server_info = strdup(server_info);
2090 /* Normalize server name */
2092 server_namec = silc_identifier_check(server_name, strlen(server_name),
2093 SILC_STRING_UTF8, 256, NULL);
2094 if (!server_namec) {
2095 silc_free(server_entry->server_name);
2096 silc_free(server_entry->server_info);
2097 silc_free(server_entry);
2102 silc_mutex_lock(conn->internal->lock);
2104 /* Add server to cache */
2105 if (!silc_idcache_add(conn->internal->server_cache, server_namec,
2106 &server_entry->id, server_entry)) {
2107 silc_free(server_namec);
2108 silc_free(server_entry->server_name);
2109 silc_free(server_entry->server_info);
2110 silc_free(server_entry);
2111 silc_mutex_unlock(conn->internal->lock);
2115 silc_mutex_unlock(conn->internal->lock);
2116 silc_client_ref_server(client, conn, server_entry);
2118 SILC_LOG_DEBUG(("Added %p", server_entry));
2120 return server_entry;
2123 /* Removes server from the cache by the server entry. */
2125 SilcBool silc_client_del_server(SilcClient client, SilcClientConnection conn,
2126 SilcServerEntry server)
2131 if (silc_atomic_sub_int32(&server->internal.deleted, 1) != 0)
2133 SILC_LOG_DEBUG(("** WARNING ** Deleting a server twice %p", server));
2138 silc_client_unref_server(client, conn, server);
2142 /* Updates the `server_entry' with the new information sent as argument. */
2144 void silc_client_update_server(SilcClient client,
2145 SilcClientConnection conn,
2146 SilcServerEntry server_entry,
2147 const char *server_name,
2148 const char *server_info)
2150 char *server_namec = NULL;
2152 SILC_LOG_DEBUG(("Updating server %p", server_entry));
2155 (!server_entry->server_name ||
2156 !silc_utf8_strcasecmp(server_entry->server_name, server_name))) {
2158 server_namec = silc_identifier_check(server_name, strlen(server_name),
2159 SILC_STRING_UTF8, 256, NULL);
2163 silc_free(server_entry->server_name);
2164 server_entry->server_name = strdup(server_name);
2165 if (!server_entry->server_name)
2168 /* Update cache entry */
2169 silc_mutex_lock(conn->internal->lock);
2170 silc_idcache_update_by_context(conn->internal->server_cache, server_entry,
2171 NULL, server_namec, TRUE);
2172 silc_mutex_unlock(conn->internal->lock);
2176 (!server_entry->server_info ||
2177 !silc_utf8_strcasecmp(server_entry->server_info, server_info))) {
2178 silc_free(server_entry->server_info);
2179 server_entry->server_info = strdup(server_info);
2185 void silc_client_lock_server(SilcServerEntry server_entry)
2187 silc_rwlock_rdlock(server_entry->internal.lock);
2192 void silc_client_unlock_server(SilcServerEntry server_entry)
2194 silc_rwlock_unlock(server_entry->internal.lock);
2197 /* Take reference of server entry */
2199 SilcServerEntry silc_client_ref_server(SilcClient client,
2200 SilcClientConnection conn,
2201 SilcServerEntry server_entry)
2203 silc_atomic_add_int32(&server_entry->internal.refcnt, 1);
2204 SILC_LOG_DEBUG(("Server %p refcnt %d->%d", server_entry,
2205 silc_atomic_get_int32(&server_entry->internal.refcnt) - 1,
2206 silc_atomic_get_int32(&server_entry->internal.refcnt)));
2207 return server_entry;
2210 /* Release reference of server entry */
2212 void silc_client_unref_server(SilcClient client, SilcClientConnection conn,
2213 SilcServerEntry server_entry)
2216 SilcIDCacheEntry id_cache;
2222 if (silc_atomic_sub_int32(&server_entry->internal.refcnt, 1) > 0)
2225 SILC_LOG_DEBUG(("Deleting server %p", server_entry));
2227 silc_mutex_lock(conn->internal->lock);
2228 if (silc_idcache_find_by_context(conn->internal->server_cache, server_entry,
2230 namec = id_cache->name;
2231 ret = silc_idcache_del_by_context(conn->internal->server_cache,
2232 server_entry, NULL);
2235 silc_mutex_unlock(conn->internal->lock);
2237 silc_free(server_entry->server_name);
2238 silc_free(server_entry->server_info);
2239 if (server_entry->public_key)
2240 silc_pkcs_public_key_free(server_entry->public_key);
2241 silc_atomic_uninit32(&server_entry->internal.refcnt);
2242 silc_rwlock_free(server_entry->internal.lock);
2243 silc_free(server_entry);
2246 /* Free server entry list */
2248 void silc_client_list_free_servers(SilcClient client,
2249 SilcClientConnection conn,
2250 SilcDList server_list)
2252 SilcServerEntry server_entry;
2255 silc_dlist_start(server_list);
2256 while ((server_entry = silc_dlist_get(server_list)))
2257 silc_client_unref_server(client, conn, server_entry);
2259 silc_dlist_uninit(server_list);