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))
89 format = (char *)nick;
91 SILC_LOG_DEBUG(("Find clients by nickname %s", parsed));
93 /* Normalize nickname for search */
94 nicknamec = silc_identifier_check(parsed, strlen(parsed),
95 SILC_STRING_UTF8, 128, NULL);
101 clients = silc_dlist_init();
103 silc_free(nicknamec);
108 silc_mutex_lock(conn->internal->lock);
110 /* Find from cache */
111 silc_list_init(list, struct SilcIDCacheEntryStruct, next);
112 if (!silc_idcache_find_by_name(conn->internal->client_cache, nicknamec,
114 silc_mutex_unlock(conn->internal->lock);
115 silc_free(nicknamec);
117 silc_dlist_uninit(clients);
120 silc_list_start(list);
123 /* Take all without any further checking */
124 while ((id_cache = silc_list_get(list))) {
125 entry = id_cache->context;
126 if (!get_valid || entry->internal.valid) {
127 silc_client_ref_client(client, conn, id_cache->context);
128 silc_dlist_add(clients, id_cache->context);
132 /* Check multiple cache entries for exact match */
133 while ((id_cache = silc_list_get(list))) {
134 entry = id_cache->context;
136 /* If server was provided, find entries that either have no server
137 set or have the same server. Ignore those that have different
139 if (server[0] && entry->server &&
140 !silc_utf8_strcasecmp(entry->server, server))
143 if (silc_utf8_strcasecmp(entry->nickname,
144 format ? format : parsed) &&
145 (!get_valid || entry->internal.valid)) {
146 silc_client_ref_client(client, conn, entry);
147 silc_dlist_add(clients, entry);
149 /* If format is NULL, we find one exact match with the base
150 nickname (parsed). */
157 silc_mutex_unlock(conn->internal->lock);
159 silc_free(nicknamec);
162 if (!silc_dlist_count(clients)) {
163 silc_dlist_uninit(clients);
167 SILC_LOG_DEBUG(("Found %d clients", silc_dlist_count(clients)));
169 silc_dlist_start(clients);
173 /* Finds clients by nickname from local cache. */
175 SilcDList silc_client_get_clients_local(SilcClient client,
176 SilcClientConnection conn,
177 const char *nickname,
180 return silc_client_get_clients_local_ext(client, conn, nickname, return_all,
184 /********************** Client Resolving from Server ************************/
186 /* Resolving context */
189 SilcGetClientCallback completion;
191 SilcClientEntry client_entry;
192 } *SilcClientGetClientInternal;
194 /* Resolving command callback */
196 static SilcBool silc_client_get_clients_cb(SilcClient client,
197 SilcClientConnection conn,
204 SilcClientGetClientInternal i = context;
205 SilcClientEntry client_entry;
207 if (error != SILC_STATUS_OK) {
208 SILC_LOG_DEBUG(("Resolving failed: %s", silc_get_status_message(error)));
210 if (i->client_entry) {
211 i->client_entry->internal.resolve_cmd_ident = 0;
212 silc_client_unref_client(client, conn, i->client_entry);
216 i->completion(client, conn, error, NULL, i->context);
220 /* Add the returned client to list */
222 client_entry = va_arg(ap, SilcClientEntry);
223 silc_client_ref_client(client, conn, client_entry);
224 silc_dlist_add(i->clients, client_entry);
225 client_entry->internal.resolve_cmd_ident = 0;
228 if (status == SILC_STATUS_OK || status == SILC_STATUS_LIST_END) {
229 /* Deliver the clients to the caller */
231 SILC_LOG_DEBUG(("Resolved %d clients", silc_dlist_count(i->clients)));
233 if (i->client_entry) {
234 i->client_entry->internal.resolve_cmd_ident = 0;
235 silc_client_unref_client(client, conn, i->client_entry);
238 silc_dlist_start(i->clients);
239 i->completion(client, conn, SILC_STATUS_OK, i->clients, i->context);
247 silc_client_list_free(client, conn, i->clients);
252 /* Resolves client information from server by the client ID. */
255 silc_client_get_client_by_id_resolve(SilcClient client,
256 SilcClientConnection conn,
257 SilcClientID *client_id,
258 SilcBuffer attributes,
259 SilcGetClientCallback completion,
262 SilcClientGetClientInternal i;
263 SilcClientEntry client_entry;
265 SilcUInt16 cmd_ident;
267 if (!client || !conn | !client_id) {
268 SILC_LOG_ERROR(("Missing arguments to "
269 "silc_client_get_clients_by_id_resolve call"));
273 SILC_LOG_DEBUG(("Resolve client by ID (%s)",
274 silc_id_render(client_id, SILC_ID_CLIENT)));
276 i = silc_calloc(1, sizeof(*i));
279 i->completion = completion;
280 i->context = context;
281 i->clients = silc_dlist_init();
287 /* Attach to resolving, if on going */
288 client_entry = silc_client_get_client_by_id(client, conn, client_id);
289 if (client_entry && client_entry->internal.resolve_cmd_ident) {
290 SILC_LOG_DEBUG(("Attach to existing resolving"));
291 silc_client_unref_client(client, conn, client_entry);
292 silc_client_command_pending(conn, SILC_COMMAND_NONE,
293 client_entry->internal.resolve_cmd_ident,
294 silc_client_get_clients_cb, i);
295 return client_entry->internal.resolve_cmd_ident;
298 /* Send the command */
299 idp = silc_id_payload_encode(client_id, SILC_ID_CLIENT);
300 cmd_ident = silc_client_command_send(client, conn, SILC_COMMAND_WHOIS,
301 silc_client_get_clients_cb, i,
302 2, 3, silc_buffer_datalen(attributes),
303 4, silc_buffer_datalen(idp));
304 if (!cmd_ident && completion)
305 completion(client, conn, SILC_STATUS_ERR_RESOURCE_LIMIT, NULL, context);
307 if (client_entry && cmd_ident) {
308 client_entry->internal.resolve_cmd_ident = cmd_ident;
309 i->client_entry = client_entry;
311 silc_client_unref_client(client, conn, client_entry);
314 silc_buffer_free(idp);
319 /* Finds client entry or entries by the `nickname' and `server'. The
320 completion callback will be called when the client entries has been
321 found. Used internally by the library. */
323 static SilcUInt16 silc_client_get_clients_i(SilcClient client,
324 SilcClientConnection conn,
326 const char *nickname,
328 SilcBuffer attributes,
329 SilcGetClientCallback completion,
332 SilcClientGetClientInternal i;
333 char nick[128 + 1], serv[256 + 1], userhost[768 + 1], *parsed = NULL;
336 SILC_LOG_DEBUG(("Resolve client by %s command",
337 silc_get_command_name(command)));
339 if (!client || !conn) {
340 SILC_LOG_ERROR(("Missing arguments to silc_client_get_clients call"));
343 if (!nickname && !attributes) {
344 SILC_LOG_ERROR(("Missing arguments to silc_client_get_clients call"));
348 /* Parse server name from the nickname if set */
349 if (silc_parse_userfqdn(nickname, nick, sizeof(nick),
350 serv, sizeof(serv)) == 2)
351 server = (const char *)serv;
352 nickname = (const char *)nick;
354 /* Parse nickname in case it is formatted */
355 if (silc_client_nickname_parse(client, conn, (char *)nickname, &parsed))
356 nickname = (const char *)parsed;
358 i = silc_calloc(1, sizeof(*i));
363 i->clients = silc_dlist_init();
369 i->completion = completion;
370 i->context = context;
372 memset(userhost, 0, sizeof(userhost));
373 if (nickname && server) {
374 len = strlen(nickname) + strlen(server) + 3;
375 silc_strncat(userhost, len, nickname, strlen(nickname));
376 silc_strncat(userhost, len, "@", 1);
377 silc_strncat(userhost, len, server, strlen(server));
378 } else if (nickname) {
379 silc_strncat(userhost, sizeof(userhost) - 1, nickname, strlen(nickname));
383 /* Send the command */
384 if (command == SILC_COMMAND_IDENTIFY)
385 return silc_client_command_send(client, conn, SILC_COMMAND_IDENTIFY,
386 silc_client_get_clients_cb, i,
387 1, 1, userhost, strlen(userhost));
388 return silc_client_command_send(client, conn, SILC_COMMAND_WHOIS,
389 silc_client_get_clients_cb, i,
390 2, 1, userhost, strlen(userhost),
391 3, silc_buffer_datalen(attributes));
394 /* Get clients from server with IDENTIFY command */
396 SilcUInt16 silc_client_get_clients(SilcClient client,
397 SilcClientConnection conn,
398 const char *nickname,
400 SilcGetClientCallback completion,
403 return silc_client_get_clients_i(client, conn, SILC_COMMAND_IDENTIFY,
404 nickname, server, NULL,
405 completion, context);
408 /* Get clients from server with WHOIS command */
410 SilcUInt16 silc_client_get_clients_whois(SilcClient client,
411 SilcClientConnection conn,
412 const char *nickname,
414 SilcBuffer attributes,
415 SilcGetClientCallback completion,
418 return silc_client_get_clients_i(client, conn, SILC_COMMAND_WHOIS,
419 nickname, server, attributes,
420 completion, context);
423 /* ID list resolving context */
425 SilcGetClientCallback completion;
427 SilcBuffer client_id_list;
428 SilcUInt32 list_count;
429 } *GetClientsByListInternal;
431 static SilcBool silc_client_get_clients_list_cb(SilcClient client,
432 SilcClientConnection conn,
439 GetClientsByListInternal i = context;
440 SilcClientEntry client_entry;
446 /* Process the list after all replies have been received */
447 if (status != SILC_STATUS_OK && !SILC_STATUS_IS_ERROR(status) &&
448 status != SILC_STATUS_LIST_END)
451 SILC_LOG_DEBUG(("Resolved all clients"));
453 clients = silc_dlist_init();
455 status = SILC_STATUS_ERR_RESOURCE_LIMIT;
459 for (c = 0; c < i->list_count; c++) {
461 SILC_GET16_MSB(idp_len, i->client_id_list->data + 2);
463 if (!silc_id_payload_parse_id(i->client_id_list->data, idp_len, &id)) {
464 status = SILC_STATUS_ERR_BAD_CLIENT_ID;
468 /* Get client entry */
469 client_entry = silc_client_get_client_by_id(client, conn, &id.u.client_id);
471 silc_dlist_add(clients, client_entry);
473 if (!silc_buffer_pull(i->client_id_list, idp_len)) {
474 status = SILC_STATUS_ERR_BAD_CLIENT_ID;
479 silc_dlist_start(clients);
480 status = SILC_STATUS_OK;
482 i->completion(client, conn, status, clients, i->context);
485 if (status != SILC_STATUS_OK && i->completion)
486 i->completion(client, conn, status, NULL, i->context);
488 silc_client_list_free(client, conn, clients);
489 silc_buffer_free(i->client_id_list);
495 /* Gets client entries by the list of client ID's `client_id_list'. This
496 always resolves those client ID's it does not know yet from the server
497 so this function might take a while. The `client_id_list' is a list
498 of ID Payloads added one after other. JOIN command reply and USERS
499 command reply for example returns this sort of list. The `completion'
500 will be called after the entries are available. */
502 SilcUInt16 silc_client_get_clients_by_list(SilcClient client,
503 SilcClientConnection conn,
504 SilcUInt32 list_count,
505 SilcBuffer client_id_list,
506 SilcGetClientCallback completion,
509 GetClientsByListInternal in;
510 SilcClientEntry entry;
511 unsigned char **res_argv = NULL;
512 SilcUInt32 *res_argv_lens = NULL, *res_argv_types = NULL, res_argc = 0;
513 SilcUInt16 idp_len, cmd_ident;
517 SILC_LOG_DEBUG(("Resolve clients from Client ID list"));
519 if (!client || !conn || !client_id_list)
522 in = silc_calloc(1, sizeof(*in));
525 in->completion = completion;
526 in->context = context;
527 in->list_count = list_count;
528 in->client_id_list = silc_buffer_copy(client_id_list);
529 if (!in->client_id_list)
532 for (i = 0; i < list_count; i++) {
534 SILC_GET16_MSB(idp_len, client_id_list->data + 2);
536 if (!silc_id_payload_parse_id(client_id_list->data, idp_len, &id))
539 /* Check if we have this client cached already. If we don't have the
540 entry or it has incomplete info, then resolve it from the server. */
541 entry = silc_client_get_client_by_id(client, conn, &id.u.client_id);
542 if (!entry || !entry->nickname[0] || !entry->username[0] ||
545 res_argv = silc_calloc(list_count, sizeof(*res_argv));
546 res_argv_lens = silc_calloc(list_count, sizeof(*res_argv_lens));
547 res_argv_types = silc_calloc(list_count, sizeof(*res_argv_types));
548 if (!res_argv || !res_argv_lens || !res_argv_types) {
549 silc_client_unref_client(client, conn, entry);
554 res_argv[res_argc] = client_id_list->data;
555 res_argv_lens[res_argc] = idp_len;
556 res_argv_types[res_argc] = res_argc + 4;
559 silc_client_unref_client(client, conn, entry);
561 if (!silc_buffer_pull(client_id_list, idp_len))
564 silc_buffer_start(client_id_list);
566 /* Query the unknown client information from server */
568 cmd_ident = silc_client_command_send_argv(client,
569 conn, SILC_COMMAND_WHOIS,
570 silc_client_get_clients_list_cb,
571 in, res_argc, res_argv,
575 silc_free(res_argv_lens);
576 silc_free(res_argv_types);
580 /* We have the clients in cache, get them and call the completion */
581 silc_client_get_clients_list_cb(client, conn, SILC_COMMAND_WHOIS,
582 SILC_STATUS_OK, SILC_STATUS_OK, in, NULL);
586 silc_buffer_free(in->client_id_list);
589 silc_free(res_argv_lens);
590 silc_free(res_argv_types);
597 SilcClientConnection conn;
598 SilcChannelID channel_id;
599 SilcGetClientCallback completion;
602 } *GetClientsByChannelInternal;
604 SILC_CLIENT_CMD_FUNC(get_clients_by_channel_cb)
606 GetClientsByChannelInternal i = context;
607 SilcClientEntry *clients = NULL;
608 SilcUInt32 clients_count = 0;
609 SilcBool found = FALSE;
610 SilcChannelEntry channel;
611 SilcHashTableList htl;
620 channel = silc_client_get_channel_by_id(i->client, i->conn, &i->channel_id);
621 if (channel && !silc_hash_table_count(channel->user_list)) {
622 clients = silc_calloc(silc_hash_table_count(channel->user_list),
624 silc_hash_table_list(channel->user_list, &htl);
625 while (silc_hash_table_get(&htl, NULL, (void *)&chu))
626 clients[clients_count++] = chu->client;
627 silc_hash_table_list_reset(&htl);
632 i->completion(i->client, i->conn, clients, clients_count, i->context);
635 i->completion(i->client, i->conn, NULL, 0, i->context);
641 /* Gets client entries by the channel entry indicated by `channel'. Thus,
642 it resolves the clients currently on that channel. */
644 void silc_client_get_clients_by_channel(SilcClient client,
645 SilcClientConnection conn,
646 SilcChannelEntry channel,
647 SilcGetClientCallback completion,
650 GetClientsByChannelInternal in;
651 SilcHashTableList htl;
653 SilcClientEntry entry;
654 unsigned char **res_argv = NULL;
655 SilcUInt32 *res_argv_lens = NULL, *res_argv_types = NULL, res_argc = 0;
657 SilcBool wait_res = FALSE;
659 assert(client && conn && channel);
661 SILC_LOG_DEBUG(("Start"));
663 in = silc_calloc(1, sizeof(*in));
666 in->channel_id = *channel->id;
667 in->completion = completion;
668 in->context = context;
670 /* If user list does not exist, send USERS command. */
671 if (!channel->user_list || !silc_hash_table_count(channel->user_list)) {
672 SILC_LOG_DEBUG(("Sending USERS"));
673 silc_client_command_register(client, SILC_COMMAND_USERS, NULL, NULL,
674 silc_client_command_reply_users_i, 0,
676 silc_client_command_send(client, conn, SILC_COMMAND_USERS,
677 conn->cmd_ident, 1, 2, channel->channel_name,
678 strlen(channel->channel_name));
679 silc_client_command_pending(conn, SILC_COMMAND_USERS, conn->cmd_ident,
680 silc_client_command_get_clients_by_channel_cb,
685 silc_hash_table_list(channel->user_list, &htl);
686 while (silc_hash_table_get(&htl, NULL, (void *)&chu)) {
689 /* If the entry has incomplete info, then resolve it from the server. */
690 if (!entry->nickname[0] || !entry->realname) {
691 if (entry->status & SILC_CLIENT_STATUS_RESOLVING) {
692 /* Attach to this resolving and wait until it finishes */
693 silc_client_command_pending(
694 conn, SILC_COMMAND_NONE,
695 entry->resolve_cmd_ident,
696 silc_client_command_get_clients_by_channel_cb,
702 entry->status |= SILC_CLIENT_STATUS_RESOLVING;
703 entry->resolve_cmd_ident = conn->cmd_ident + 1;
705 idp = silc_id_payload_encode(entry->id, SILC_ID_CLIENT);
707 /* No we don't have it, query it from the server. Assemble argument
708 table that will be sent for the WHOIS command later. */
709 res_argv = silc_realloc(res_argv, sizeof(*res_argv) *
711 res_argv_lens = silc_realloc(res_argv_lens, sizeof(*res_argv_lens) *
713 res_argv_types = silc_realloc(res_argv_types, sizeof(*res_argv_types) *
715 res_argv[res_argc] = silc_memdup(idp->data, idp->len);
716 res_argv_lens[res_argc] = idp->len;
717 res_argv_types[res_argc] = res_argc + 4;
720 silc_buffer_free(idp);
723 silc_hash_table_list_reset(&htl);
725 /* Query the client information from server if the list included clients
726 that we don't know about. */
730 /* Send the WHOIS command to server */
731 res_cmd = silc_command_payload_encode(SILC_COMMAND_WHOIS,
732 res_argc, res_argv, res_argv_lens,
733 res_argv_types, ++conn->cmd_ident);
734 silc_client_packet_send(client, conn->sock, SILC_PACKET_COMMAND,
735 NULL, 0, NULL, NULL, res_cmd->data, res_cmd->len,
738 /* Register our own command reply for this command */
739 silc_client_command_register(client, SILC_COMMAND_WHOIS, NULL, NULL,
740 silc_client_command_reply_whois_i, 0,
743 /* Process the applications request after reply has been received */
744 silc_client_command_pending(
745 conn, SILC_COMMAND_WHOIS, conn->cmd_ident,
746 silc_client_command_get_clients_by_channel_cb,
750 silc_buffer_free(res_cmd);
752 silc_free(res_argv_lens);
753 silc_free(res_argv_types);
760 /* We have the clients in cache, get them and call the completion */
761 silc_client_command_get_clients_by_channel_cb((void *)in, NULL);
766 /************************** Client Entry Routines ***************************/
768 /* Creates new client entry and adds it to the ID cache. Returns pointer
771 SilcClientEntry silc_client_add_client(SilcClient client,
772 SilcClientConnection conn,
773 char *nickname, char *username,
774 char *userinfo, SilcClientID *id,
777 SilcClientEntry client_entry;
778 char *nick = NULL, parsed[128 + 1];
780 SILC_LOG_DEBUG(("Adding new client entry"));
782 /* Save the client infos */
783 client_entry = silc_calloc(1, sizeof(*client_entry));
787 silc_rwlock_alloc(&client_entry->internal.lock);
788 silc_atomic_init16(&client_entry->internal.refcnt, 0);
789 client_entry->id = *id;
790 client_entry->mode = mode;
791 client_entry->realname = userinfo ? strdup(userinfo) : NULL;
793 silc_parse_userfqdn(nickname, parsed, sizeof(parsed),
794 client_entry->server, sizeof(client_entry->server));
795 if (nickname && client->internal->params->full_nicknames)
796 silc_snprintf(client_entry->nickname, sizeof(client_entry->nickname),
799 silc_snprintf(client_entry->nickname, sizeof(client_entry->nickname),
802 silc_parse_userfqdn(username, client_entry->username,
803 sizeof(client_entry->username),
804 client_entry->hostname,
805 sizeof(client_entry->hostname));
807 client_entry->channels = silc_hash_table_alloc(NULL, 1, silc_hash_ptr, NULL,
808 NULL, NULL, NULL, NULL, TRUE);
809 if (!client_entry->channels) {
810 silc_free(client_entry->realname);
811 silc_free(client_entry);
815 /* Normalize nickname */
816 if (client_entry->nickname[0]) {
817 nick = silc_identifier_check(parsed, strlen(parsed),
818 SILC_STRING_UTF8, 128, NULL);
820 silc_free(client_entry->realname);
821 silc_hash_table_free(client_entry->channels);
822 silc_free(client_entry);
827 silc_mutex_lock(conn->internal->lock);
829 /* Add client to cache, the normalized nickname is saved to cache */
830 if (!silc_idcache_add(conn->internal->client_cache, nick,
831 &client_entry->id, client_entry)) {
833 silc_free(client_entry->realname);
834 silc_hash_table_free(client_entry->channels);
835 silc_free(client_entry);
836 silc_mutex_unlock(conn->internal->lock);
840 client_entry->nickname_normalized = nick;
842 silc_mutex_unlock(conn->internal->lock);
843 silc_client_ref_client(client, conn, client_entry);
845 /* Format the nickname */
846 silc_client_nickname_format(client, conn, client_entry, FALSE);
848 if (client_entry->nickname[0])
849 client_entry->internal.valid = TRUE;
851 SILC_LOG_DEBUG(("Added %p", client_entry));
856 /* Updates the `client_entry' with the new information sent as argument.
857 This handles entry locking internally. */
859 void silc_client_update_client(SilcClient client,
860 SilcClientConnection conn,
861 SilcClientEntry client_entry,
862 const char *nickname,
863 const char *username,
864 const char *userinfo,
867 char *nick = NULL, parsed[128 + 1];
869 SILC_LOG_DEBUG(("Update client entry"));
871 silc_rwlock_wrlock(client_entry->internal.lock);
873 if (!client_entry->realname && userinfo)
874 client_entry->realname = strdup(userinfo);
876 if ((!client_entry->username[0] || !client_entry->hostname[0]) && username)
877 silc_parse_userfqdn(username, client_entry->username,
878 sizeof(client_entry->username),
879 client_entry->hostname,
880 sizeof(client_entry->username));
882 if (!client_entry->nickname[0] && nickname) {
883 silc_parse_userfqdn(nickname, parsed, sizeof(parsed),
884 client_entry->server, sizeof(client_entry->server));
885 if (client->internal->params->full_nicknames)
886 silc_snprintf(client_entry->nickname, sizeof(client_entry->nickname),
889 silc_snprintf(client_entry->nickname, sizeof(client_entry->nickname),
892 /* Normalize nickname */
893 nick = silc_identifier_check(parsed, strlen(parsed),
894 SILC_STRING_UTF8, 128, NULL);
896 silc_rwlock_unlock(client_entry->internal.lock);
900 /* Format nickname */
901 silc_client_nickname_format(client, conn, client_entry,
902 client_entry == conn->local_entry);
904 /* Update cache entry */
905 silc_mutex_lock(conn->internal->lock);
906 silc_idcache_update_by_context(conn->internal->client_cache,
907 client_entry, NULL, nick, TRUE);
908 silc_mutex_unlock(conn->internal->lock);
909 client_entry->nickname_normalized = nick;
910 client_entry->internal.valid = TRUE;
912 client_entry->mode = mode;
914 silc_rwlock_unlock(client_entry->internal.lock);
917 /* Change a client's nickname. Must be called with `client_entry' locked. */
919 SilcBool silc_client_change_nickname(SilcClient client,
920 SilcClientConnection conn,
921 SilcClientEntry client_entry,
922 const char *new_nick,
923 SilcClientID *new_id,
924 const unsigned char *idp,
929 SILC_LOG_DEBUG(("Change nickname %s to %s", client_entry->nickname,
932 /* Normalize nickname */
933 tmp = silc_identifier_check(new_nick, strlen(new_nick),
934 SILC_STRING_UTF8, 128, NULL);
938 /* Update the client entry */
939 silc_mutex_lock(conn->internal->lock);
940 if (!silc_idcache_update_by_context(conn->internal->client_cache,
941 client_entry, new_id, tmp, TRUE)) {
943 silc_mutex_unlock(conn->internal->lock);
946 silc_mutex_unlock(conn->internal->lock);
948 memset(client_entry->nickname, 0, sizeof(client_entry->nickname));
949 memcpy(client_entry->nickname, new_nick, strlen(new_nick));
950 client_entry->nickname_normalized = tmp;
951 silc_client_nickname_format(client, conn, client_entry,
952 client_entry == conn->local_entry);
954 /* For my client entry, update ID and set new ID to packet stream */
955 if (client_entry == conn->local_entry) {
956 if (idp && idp_len) {
957 silc_buffer_enlarge(conn->internal->local_idp, idp_len);
958 silc_buffer_put(conn->internal->local_idp, idp, idp_len);
961 silc_packet_set_ids(conn->stream, SILC_ID_CLIENT, conn->local_id,
965 client_entry->internal.valid = TRUE;
969 /* Deletes the client entry and frees all memory. */
971 void silc_client_del_client_entry(SilcClient client,
972 SilcClientConnection conn,
973 SilcClientEntry client_entry)
975 silc_free(client_entry->realname);
976 silc_free(client_entry->nickname_normalized);
977 silc_free(client_entry->internal.key);
978 if (client_entry->public_key)
979 silc_pkcs_public_key_free(client_entry->public_key);
980 silc_hash_table_free(client_entry->channels);
981 if (client_entry->internal.send_key)
982 silc_cipher_free(client_entry->internal.send_key);
983 if (client_entry->internal.receive_key)
984 silc_cipher_free(client_entry->internal.receive_key);
985 if (client_entry->internal.hmac_send)
986 silc_hmac_free(client_entry->internal.hmac_send);
987 if (client_entry->internal.hmac_receive)
988 silc_hmac_free(client_entry->internal.hmac_receive);
989 silc_client_ftp_session_free_client(client, client_entry);
990 if (client_entry->internal.ke)
991 silc_client_abort_key_agreement(client, conn, client_entry);
992 silc_atomic_uninit16(&client_entry->internal.refcnt);
993 silc_rwlock_free(client_entry->internal.lock);
994 silc_free(client_entry);
997 /* Removes client from the cache by the client entry. */
999 SilcBool silc_client_del_client(SilcClient client, SilcClientConnection conn,
1000 SilcClientEntry client_entry)
1007 if (silc_atomic_sub_int16(&client_entry->internal.refcnt, 1) > 0)
1010 SILC_LOG_DEBUG(("Deleting client %p", client_entry));
1012 silc_mutex_lock(conn->internal->lock);
1013 ret = silc_idcache_del_by_context(conn->internal->client_cache,
1014 client_entry, NULL);
1015 silc_mutex_unlock(conn->internal->lock);
1018 /* Remove from channels */
1019 silc_client_remove_from_channels(client, conn, client_entry);
1021 /* Free the client entry data */
1022 silc_client_del_client_entry(client, conn, client_entry);
1028 /* Internal routine used to find client by ID and if not found this creates
1029 new client entry and returns it. */
1031 SilcClientEntry silc_client_get_client(SilcClient client,
1032 SilcClientConnection conn,
1033 SilcClientID *client_id)
1035 SilcClientEntry client_entry;
1037 client_entry = silc_client_get_client_by_id(client, conn, client_id);
1038 if (!client_entry) {
1039 client_entry = silc_client_add_client(client, conn, NULL, NULL, NULL,
1043 silc_client_ref_client(client, conn, client_entry);
1046 return client_entry;
1051 void silc_client_lock_client(SilcClientEntry client_entry)
1053 silc_rwlock_rdlock(client_entry->internal.lock);
1058 void silc_client_unlock_client(SilcClientEntry client_entry)
1060 silc_rwlock_unlock(client_entry->internal.lock);
1063 /* Take reference of client entry */
1065 SilcClientEntry silc_client_ref_client(SilcClient client,
1066 SilcClientConnection conn,
1067 SilcClientEntry client_entry)
1069 silc_atomic_add_int16(&client_entry->internal.refcnt, 1);
1070 SILC_LOG_DEBUG(("Client %p refcnt %d->%d", client_entry,
1071 silc_atomic_get_int16(&client_entry->internal.refcnt) - 1,
1072 silc_atomic_get_int16(&client_entry->internal.refcnt)));
1073 return client_entry;
1076 /* Release reference of client entry */
1078 void silc_client_unref_client(SilcClient client, SilcClientConnection conn,
1079 SilcClientEntry client_entry)
1082 SILC_LOG_DEBUG(("Client %p refcnt %d->%d", client_entry,
1083 silc_atomic_get_int16(&client_entry->internal.refcnt),
1084 silc_atomic_get_int16(&client_entry->internal.refcnt) - 1));
1085 silc_client_del_client(client, conn, client_entry);
1089 /* Free client entry list */
1091 void silc_client_list_free(SilcClient client, SilcClientConnection conn,
1092 SilcDList client_list)
1094 SilcClientEntry client_entry;
1097 silc_dlist_start(client_list);
1098 while ((client_entry = silc_dlist_get(client_list)))
1099 silc_client_unref_client(client, conn, client_entry);
1101 silc_dlist_uninit(client_list);
1105 /* Formats the nickname of the client specified by the `client_entry'.
1106 If the format is specified by the application this will format the
1107 nickname and replace the old nickname in the client entry. If the
1108 format string is not specified then this function has no effect.
1109 Returns the client entry that was formatted. */
1111 SilcClientEntry silc_client_nickname_format(SilcClient client,
1112 SilcClientConnection conn,
1113 SilcClientEntry client_entry,
1117 char newnick[128 + 1];
1118 int i, off = 0, len;
1120 SilcClientEntry entry, unformatted = NULL;
1121 SilcBool formatted = FALSE;
1123 if (!client->internal->params->nickname_format[0])
1124 return client_entry;
1125 if (!client_entry->nickname[0])
1128 SILC_LOG_DEBUG(("Format nickname"));
1130 /* Get all clients with same nickname. Do not perform the formatting
1131 if there aren't any clients with same nickname unless the application
1132 is forcing us to do so. */
1133 clients = silc_client_get_clients_local_ext(client, conn,
1134 client_entry->nickname,
1138 if (silc_dlist_count(clients) == 1 && !priority &&
1139 !client->internal->params->nickname_force_format) {
1140 silc_client_list_free(client, conn, clients);
1141 return client_entry;
1144 /* Is the requested client formatted already */
1145 if (client_entry->nickname_normalized &&
1146 !silc_utf8_strcasecmp(client_entry->nickname,
1147 client_entry->nickname_normalized))
1150 if (client->internal->params->nickname_force_format)
1153 /* Find unformatted client entry */
1154 while ((entry = silc_dlist_get(clients))) {
1155 if (!entry->internal.valid)
1157 if (entry == client_entry)
1159 if (silc_utf8_strcasecmp(entry->nickname, entry->nickname_normalized)) {
1160 unformatted = entry;
1165 /* If there are no other unformatted clients and the requested client is
1166 unformatted, just return it. */
1167 if (!unformatted && !formatted) {
1168 silc_client_list_free(client, conn, clients);
1169 return client_entry;
1172 /* If priority formatting then the requested client will get the
1173 unformatted nickname, and the unformatted client will get a new
1174 formatted nickname. */
1177 /* Simply change the client's nickname to unformatted */
1178 if (!silc_client_nickname_parse(client, conn, client_entry->nickname,
1182 silc_snprintf(client_entry->nickname, sizeof(client_entry->nickname),
1188 /* There was no other unformatted client */
1189 silc_client_list_free(client, conn, clients);
1190 return client_entry;
1193 /* Now format the previously unformatted client */
1194 client_entry = unformatted;
1198 /* If already formatted just return it */
1200 silc_client_list_free(client, conn, clients);
1201 return client_entry;
1204 memset(newnick, 0, sizeof(newnick));
1205 cp = client->internal->params->nickname_format;
1215 if (!client_entry->nickname[0])
1217 len = strlen(client_entry->nickname);
1218 memcpy(&newnick[off], client_entry->nickname, len);
1222 /* Stripped hostname */
1223 if (!client_entry->hostname[0])
1225 len = strcspn(client_entry->hostname, ".");
1226 i = strcspn(client_entry->hostname, "-");
1229 memcpy(&newnick[off], client_entry->hostname, len);
1234 if (!client_entry->hostname[0])
1236 len = strlen(client_entry->hostname);
1237 memcpy(&newnick[off], client_entry->hostname, len);
1241 /* Ascending number */
1246 if (silc_dlist_count(clients) == 1)
1249 silc_dlist_start(clients);
1250 while ((entry = silc_dlist_get(clients))) {
1251 if (!silc_utf8_strncasecmp(entry->nickname, newnick, off))
1253 if (strlen(entry->nickname) <= off)
1255 num = atoi(&entry->nickname[off]);
1260 memset(tmp, 0, sizeof(tmp));
1261 silc_snprintf(tmp, sizeof(tmp) - 1, "%d", ++max);
1263 memcpy(&newnick[off], tmp, len);
1268 /* Some other character in the string */
1269 memcpy(&newnick[off], cp, 1);
1278 memcpy(client_entry->nickname, newnick, strlen(newnick));
1279 silc_client_list_free(client, conn, clients);
1281 return client_entry;
1284 /* Parses nickname according to nickname format string */
1286 SilcBool silc_client_nickname_parse(SilcClient client,
1287 SilcClientConnection conn,
1291 char *cp, s = 0, e = 0, *nick;
1295 if (!client->internal->params->nickname_format[0]) {
1296 *ret_nick = silc_strdup(nickname);
1300 if (!nickname || !nickname[0])
1303 cp = client->internal->params->nickname_format;
1321 /* Get separator character */
1334 /* Parse the nickname */
1338 if (strchr(nickname, s))
1339 nick = strchr(nickname, s) + 1;
1341 if (strchr(nick, e))
1342 len = strchr(nick, e) - nick;
1346 *ret_nick = silc_memdup(nick, len);
1350 SILC_LOG_DEBUG(("Parsed nickname: %s", *ret_nick));
1355 /************************ Channel Searching Locally *************************/
1357 /* Finds entry for channel by the channel name. Returns the entry or NULL
1358 if the entry was not found. It is found only if the client is joined
1361 SilcChannelEntry silc_client_get_channel(SilcClient client,
1362 SilcClientConnection conn,
1366 SilcIDCacheEntry id_cache;
1367 SilcChannelEntry entry = NULL;
1368 char chname[256 + 1], server[256 + 1];
1370 if (!client || !conn || !channel)
1373 SILC_LOG_DEBUG(("Find channel %s", channel));
1375 /* Parse server name from channel name */
1376 silc_parse_userfqdn(channel, chname, sizeof(chname), server, sizeof(server));
1378 /* Normalize name for search */
1379 channel = silc_channel_name_check(chname, strlen(chname), SILC_STRING_UTF8,
1384 silc_mutex_lock(conn->internal->lock);
1386 if (!silc_idcache_find_by_name(conn->internal->channel_cache, channel,
1388 silc_mutex_unlock(conn->internal->lock);
1393 /* If server name was specified with channel name, find the correct
1394 channel entry with the server name. There can only be one channel
1395 with same name on same server. */
1396 silc_list_start(list);
1398 while ((id_cache = silc_list_get(list))) {
1399 entry = id_cache->context;
1400 if (!entry->server[0])
1402 if (silc_utf8_strcasecmp(entry->server, server))
1406 /* Get first channel without server name specified or one with our
1407 current server connection name */
1408 while ((id_cache = silc_list_get(list))) {
1409 entry = id_cache->context;
1410 if (!entry->server[0])
1412 if (silc_utf8_strcasecmp(entry->server, conn->remote_host))
1418 silc_mutex_unlock(conn->internal->lock);
1423 SILC_LOG_DEBUG(("Found channel %s%s%s", entry->channel_name,
1424 entry->server[0] ? "@" : "", entry->server));
1427 silc_client_ref_channel(client, conn, entry);
1428 silc_mutex_unlock(conn->internal->lock);
1435 /* Finds entry for channel by the channel ID. Returns the entry or NULL
1436 if the entry was not found. It is found only if the client is joined
1439 SilcChannelEntry silc_client_get_channel_by_id(SilcClient client,
1440 SilcClientConnection conn,
1441 SilcChannelID *channel_id)
1443 SilcIDCacheEntry id_cache;
1444 SilcChannelEntry entry;
1446 if (!client || !conn || !channel_id)
1449 SILC_LOG_DEBUG(("Find channel by id %s",
1450 silc_id_render(channel_id, SILC_ID_CHANNEL)));
1452 silc_mutex_lock(conn->internal->lock);
1454 if (!silc_idcache_find_by_id_one(conn->internal->channel_cache, channel_id,
1456 silc_mutex_unlock(conn->internal->lock);
1460 SILC_LOG_DEBUG(("Found"));
1462 entry = id_cache->context;
1465 silc_client_ref_channel(client, conn, entry);
1466 silc_mutex_unlock(conn->internal->lock);
1471 /********************** Channel Resolving from Server ***********************/
1473 /* Channel resolving context */
1476 SilcGetChannelCallback completion;
1478 } *SilcClientGetChannelInternal;
1480 /* Resolving command callback */
1482 static SilcBool silc_client_get_channel_cb(SilcClient client,
1483 SilcClientConnection conn,
1484 SilcCommand command,
1490 SilcClientGetChannelInternal i = context;
1491 SilcChannelEntry entry;
1493 if (error != SILC_STATUS_OK) {
1494 SILC_LOG_DEBUG(("Resolving failed: %s", silc_get_status_message(error)));
1496 i->completion(client, conn, error, NULL, i->context);
1500 /* Add the returned channel to list */
1501 if (i->completion) {
1502 entry = va_arg(ap, SilcChannelEntry);
1503 silc_client_ref_channel(client, conn, entry);
1504 silc_dlist_add(i->channels, entry);
1507 if (status == SILC_STATUS_OK || status == SILC_STATUS_LIST_END) {
1508 /* Deliver the channels to the caller */
1509 if (i->completion) {
1510 SILC_LOG_DEBUG(("Resolved %d channels", silc_dlist_count(i->channels)));
1511 silc_dlist_start(i->channels);
1512 i->completion(client, conn, SILC_STATUS_OK, i->channels, i->context);
1520 silc_client_list_free_channels(client, conn, i->channels);
1525 /* Resolves channel entry from the server by the channel name. */
1527 void silc_client_get_channel_resolve(SilcClient client,
1528 SilcClientConnection conn,
1530 SilcGetChannelCallback completion,
1533 SilcClientGetChannelInternal i;
1535 if (!client || !conn || !channel_name || !completion)
1538 SILC_LOG_DEBUG(("Resolve channel %s", channel_name));
1540 i = silc_calloc(1, sizeof(*i));
1543 i->completion = completion;
1544 i->context = context;
1545 i->channels = silc_dlist_init();
1551 /* Send the command */
1552 if (!silc_client_command_send(client, conn, SILC_COMMAND_IDENTIFY,
1553 silc_client_get_channel_cb, i, 1,
1554 3, channel_name, strlen(channel_name))) {
1556 completion(client, conn, SILC_STATUS_ERR_RESOURCE_LIMIT, NULL, context);
1560 /* Resolves channel information from the server by the channel ID. */
1563 silc_client_get_channel_by_id_resolve(SilcClient client,
1564 SilcClientConnection conn,
1565 SilcChannelID *channel_id,
1566 SilcGetChannelCallback completion,
1569 SilcClientGetChannelInternal i;
1571 SilcUInt16 cmd_ident;
1573 if (!client || !conn || !channel_id || !completion)
1576 SILC_LOG_DEBUG(("Resolve channel by id %s",
1577 silc_id_render(channel_id, SILC_ID_CHANNEL)));
1579 i = silc_calloc(1, sizeof(*i));
1582 i->completion = completion;
1583 i->context = context;
1584 i->channels = silc_dlist_init();
1590 /* Send the command */
1591 idp = silc_id_payload_encode(channel_id, SILC_ID_CHANNEL);
1592 cmd_ident = silc_client_command_send(client, conn, SILC_COMMAND_IDENTIFY,
1593 silc_client_get_channel_cb, i, 1,
1594 5, silc_buffer_datalen(idp));
1595 silc_buffer_free(idp);
1596 if (!cmd_ident && completion)
1597 completion(client, conn, SILC_STATUS_ERR_RESOURCE_LIMIT, NULL, context);
1602 /************************* Channel Entry Routines ***************************/
1604 /* Add new channel entry to the ID Cache */
1606 SilcChannelEntry silc_client_add_channel(SilcClient client,
1607 SilcClientConnection conn,
1608 const char *channel_name,
1610 SilcChannelID *channel_id)
1612 SilcChannelEntry channel;
1613 char *channel_namec, name[256 + 1];
1615 SILC_LOG_DEBUG(("Adding channel %s", channel_name));
1617 channel = silc_calloc(1, sizeof(*channel));
1621 silc_rwlock_alloc(&channel->internal.lock);
1622 silc_atomic_init16(&channel->internal.refcnt, 0);
1623 channel->id = *channel_id;
1624 channel->mode = mode;
1626 silc_parse_userfqdn(channel_name, name, sizeof(name),
1627 channel->server, sizeof(channel->server));
1628 if (client->internal->params->full_channel_names)
1629 channel->channel_name = strdup(channel_name);
1631 channel->channel_name = strdup(name);
1633 if (!channel->channel_name) {
1634 silc_rwlock_free(channel->internal.lock);
1635 silc_atomic_uninit16(&channel->internal.refcnt);
1640 channel->user_list = silc_hash_table_alloc(NULL, 1, silc_hash_ptr, NULL, NULL,
1641 NULL, NULL, NULL, TRUE);
1642 if (!channel->user_list) {
1643 silc_rwlock_free(channel->internal.lock);
1644 silc_atomic_uninit16(&channel->internal.refcnt);
1645 silc_free(channel->channel_name);
1650 /* Normalize channel name */
1651 channel_namec = silc_channel_name_check(name, strlen(name),
1652 SILC_STRING_UTF8, 256, NULL);
1653 if (!channel_namec) {
1654 silc_rwlock_free(channel->internal.lock);
1655 silc_atomic_uninit16(&channel->internal.refcnt);
1656 silc_free(channel->channel_name);
1657 silc_hash_table_free(channel->user_list);
1662 silc_mutex_lock(conn->internal->lock);
1664 /* Add channel to cache, the normalized channel name is saved to cache */
1665 if (!silc_idcache_add(conn->internal->channel_cache, channel_namec,
1666 &channel->id, channel)) {
1667 silc_rwlock_free(channel->internal.lock);
1668 silc_atomic_uninit16(&channel->internal.refcnt);
1669 silc_free(channel_namec);
1670 silc_free(channel->channel_name);
1671 silc_hash_table_free(channel->user_list);
1673 silc_mutex_unlock(conn->internal->lock);
1677 silc_mutex_unlock(conn->internal->lock);
1678 silc_client_ref_channel(client, conn, channel);
1680 SILC_LOG_DEBUG(("Added %p", channel));
1685 /* Removes channel from the cache by the channel entry. */
1687 SilcBool silc_client_del_channel(SilcClient client, SilcClientConnection conn,
1688 SilcChannelEntry channel)
1690 SilcIDCacheEntry id_cache;
1691 SilcBool ret = TRUE;
1699 if (silc_atomic_sub_int16(&channel->internal.refcnt, 1) > 0)
1702 SILC_LOG_DEBUG(("Deleting channel %p", channel));
1704 silc_mutex_lock(conn->internal->lock);
1705 if (silc_idcache_find_by_context(conn->internal->channel_cache, channel,
1707 namec = id_cache->name;
1708 ret = silc_idcache_del_by_context(conn->internal->channel_cache,
1712 silc_mutex_unlock(conn->internal->lock);
1717 silc_client_empty_channel(client, conn, channel);
1718 silc_client_del_channel_private_keys(client, conn, channel);
1719 silc_hash_table_free(channel->user_list);
1720 silc_free(channel->channel_name);
1721 silc_free(channel->topic);
1722 if (channel->founder_key)
1723 silc_pkcs_public_key_free(channel->founder_key);
1724 if (channel->internal.send_key)
1725 silc_cipher_free(channel->internal.send_key);
1726 if (channel->internal.receive_key)
1727 silc_cipher_free(channel->internal.receive_key);
1728 if (channel->internal.hmac)
1729 silc_hmac_free(channel->internal.hmac);
1730 if (channel->internal.old_channel_keys) {
1731 silc_dlist_start(channel->internal.old_channel_keys);
1732 while ((key = silc_dlist_get(channel->internal.old_channel_keys)))
1733 silc_cipher_free(key);
1734 silc_dlist_uninit(channel->internal.old_channel_keys);
1736 if (channel->internal.old_hmacs) {
1737 silc_dlist_start(channel->internal.old_hmacs);
1738 while ((hmac = silc_dlist_get(channel->internal.old_hmacs)))
1739 silc_hmac_free(hmac);
1740 silc_dlist_uninit(channel->internal.old_hmacs);
1742 if (channel->channel_pubkeys)
1743 silc_argument_list_free(channel->channel_pubkeys,
1744 SILC_ARGUMENT_PUBLIC_KEY);
1745 silc_atomic_uninit16(&channel->internal.refcnt);
1746 silc_rwlock_free(channel->internal.lock);
1747 silc_schedule_task_del_by_context(conn->client->schedule, channel);
1753 /* Replaces the channel ID of the `channel' to `new_id'. Returns FALSE
1754 if the ID could not be changed. This handles entry locking internally. */
1756 SilcBool silc_client_replace_channel_id(SilcClient client,
1757 SilcClientConnection conn,
1758 SilcChannelEntry channel,
1759 SilcChannelID *new_id)
1761 SilcBool ret = FALSE;
1766 SILC_LOG_DEBUG(("Old Channel ID id(%s)",
1767 silc_id_render(&channel->id, SILC_ID_CHANNEL)));
1768 SILC_LOG_DEBUG(("New Channel ID id(%s)",
1769 silc_id_render(new_id, SILC_ID_CHANNEL)));
1772 silc_rwlock_wrlock(channel->internal.lock);
1773 silc_mutex_lock(conn->internal->lock);
1774 silc_idcache_update_by_context(conn->internal->channel_cache, channel,
1775 new_id, NULL, FALSE);
1776 silc_mutex_unlock(conn->internal->lock);
1777 silc_rwlock_unlock(channel->internal.lock);
1784 void silc_client_lock_channel(SilcChannelEntry channel_entry)
1786 silc_rwlock_rdlock(channel_entry->internal.lock);
1789 /* Unlock channel */
1791 void silc_client_unlock_channel(SilcChannelEntry channel_entry)
1793 silc_rwlock_unlock(channel_entry->internal.lock);
1796 /* Take reference of channel entry */
1798 SilcChannelEntry silc_client_ref_channel(SilcClient client,
1799 SilcClientConnection conn,
1800 SilcChannelEntry channel_entry)
1802 silc_atomic_add_int16(&channel_entry->internal.refcnt, 1);
1803 SILC_LOG_DEBUG(("Channel %p refcnt %d->%d", channel_entry,
1804 silc_atomic_get_int16(&channel_entry->internal.refcnt) - 1,
1805 silc_atomic_get_int16(&channel_entry->internal.refcnt)));
1806 return channel_entry;
1809 /* Release reference of channel entry */
1811 void silc_client_unref_channel(SilcClient client, SilcClientConnection conn,
1812 SilcChannelEntry channel_entry)
1814 if (channel_entry) {
1815 SILC_LOG_DEBUG(("Channel %p refcnt %d->%d", channel_entry,
1816 silc_atomic_get_int16(&channel_entry->internal.refcnt),
1817 silc_atomic_get_int16(&channel_entry->internal.refcnt)
1819 silc_client_del_channel(client, conn, channel_entry);
1823 /* Free channel entry list */
1825 void silc_client_list_free_channels(SilcClient client,
1826 SilcClientConnection conn,
1827 SilcDList channel_list)
1829 SilcChannelEntry channel_entry;
1832 silc_dlist_start(channel_list);
1833 while ((channel_entry = silc_dlist_get(channel_list)))
1834 silc_client_unref_channel(client, conn, channel_entry);
1836 silc_dlist_uninit(channel_list);
1840 /************************* Server Searching Locally *************************/
1842 /* Finds entry for server by the server name. */
1844 SilcServerEntry silc_client_get_server(SilcClient client,
1845 SilcClientConnection conn,
1848 SilcIDCacheEntry id_cache;
1849 SilcServerEntry entry;
1851 if (!client || !conn || !server_name)
1854 SILC_LOG_DEBUG(("Find server by name %s", server_name));
1856 /* Normalize server name for search */
1857 server_name = silc_identifier_check(server_name, strlen(server_name),
1858 SILC_STRING_UTF8, 256, NULL);
1862 silc_mutex_lock(conn->internal->lock);
1864 if (!silc_idcache_find_by_name_one(conn->internal->server_cache,
1865 server_name, &id_cache)) {
1866 silc_free(server_name);
1867 silc_mutex_unlock(conn->internal->lock);
1871 SILC_LOG_DEBUG(("Found"));
1874 entry = id_cache->context;
1875 silc_client_ref_server(client, conn, entry);
1877 silc_mutex_unlock(conn->internal->lock);
1879 silc_free(server_name);
1884 /* Finds entry for server by the server ID. */
1886 SilcServerEntry silc_client_get_server_by_id(SilcClient client,
1887 SilcClientConnection conn,
1888 SilcServerID *server_id)
1890 SilcIDCacheEntry id_cache;
1891 SilcServerEntry entry;
1893 if (!client || !conn || !server_id)
1896 SILC_LOG_DEBUG(("Find server by id %s",
1897 silc_id_render(server_id, SILC_ID_SERVER)));
1899 silc_mutex_lock(conn->internal->lock);
1901 if (!silc_idcache_find_by_id_one(conn->internal->server_cache,
1902 server_id, &id_cache)) {
1903 silc_mutex_unlock(conn->internal->lock);
1907 SILC_LOG_DEBUG(("Found"));
1910 entry = id_cache->context;
1911 silc_client_ref_server(client, conn, entry);
1913 silc_mutex_unlock(conn->internal->lock);
1918 /*********************** Server Resolving from Server ***********************/
1920 /* Resolving context */
1923 SilcGetServerCallback completion;
1925 } *SilcClientGetServerInternal;
1927 /* Resolving command callback */
1929 static SilcBool silc_client_get_server_cb(SilcClient client,
1930 SilcClientConnection conn,
1931 SilcCommand command,
1937 SilcClientGetServerInternal i = context;
1938 SilcServerEntry server;
1940 if (error != SILC_STATUS_OK) {
1941 SILC_LOG_DEBUG(("Resolving failed: %s", silc_get_status_message(error)));
1943 i->completion(client, conn, error, NULL, i->context);
1947 /* Add the returned servers to list */
1948 if (i->completion) {
1949 server = va_arg(ap, SilcServerEntry);
1950 silc_client_ref_server(client, conn, server);
1951 silc_dlist_add(i->servers, server);
1952 server->internal.resolve_cmd_ident = 0;
1955 if (status == SILC_STATUS_OK || status == SILC_STATUS_LIST_END) {
1956 /* Deliver the servers to the caller */
1957 if (i->completion) {
1958 SILC_LOG_DEBUG(("Resolved %d servers", silc_dlist_count(i->servers)));
1959 silc_dlist_start(i->servers);
1960 i->completion(client, conn, SILC_STATUS_OK, i->servers, i->context);
1968 silc_client_list_free_servers(client, conn, i->servers);
1973 /* Resolve server by server ID */
1976 silc_client_get_server_by_id_resolve(SilcClient client,
1977 SilcClientConnection conn,
1978 SilcServerID *server_id,
1979 SilcGetServerCallback completion,
1982 SilcClientGetServerInternal i;
1983 SilcServerEntry server;
1985 SilcUInt16 cmd_ident;
1987 if (!client || !conn || !server_id || !completion)
1990 SILC_LOG_DEBUG(("Resolve server by id %s",
1991 silc_id_render(server_id, SILC_ID_SERVER)));
1993 i = silc_calloc(1, sizeof(*i));
1996 i->completion = completion;
1997 i->context = context;
1998 i->servers = silc_dlist_init();
2004 /* Attach to resolving, if on going */
2005 server = silc_client_get_server_by_id(client, conn, server_id);
2006 if (server && server->internal.resolve_cmd_ident) {
2007 SILC_LOG_DEBUG(("Attach to existing resolving"));
2008 silc_client_unref_server(client, conn, server);
2009 silc_client_command_pending(conn, SILC_COMMAND_NONE,
2010 server->internal.resolve_cmd_ident,
2011 silc_client_get_server_cb, i);
2012 return server->internal.resolve_cmd_ident;
2015 /* Send the command */
2016 idp = silc_id_payload_encode(server_id, SILC_ID_SERVER);
2017 cmd_ident = silc_client_command_send(client, conn, SILC_COMMAND_IDENTIFY,
2018 silc_client_get_server_cb, i, 1,
2019 5, silc_buffer_datalen(idp));
2020 silc_buffer_free(idp);
2021 if (!cmd_ident && completion)
2022 completion(client, conn, SILC_STATUS_ERR_RESOURCE_LIMIT, NULL, context);
2024 if (server && cmd_ident)
2025 server->internal.resolve_cmd_ident = cmd_ident;
2027 silc_client_unref_server(client, conn, server);
2032 /************************** Server Entry Routines ***************************/
2034 /* Add new server entry */
2036 SilcServerEntry silc_client_add_server(SilcClient client,
2037 SilcClientConnection conn,
2038 const char *server_name,
2039 const char *server_info,
2040 SilcServerID *server_id)
2042 SilcServerEntry server_entry;
2043 char *server_namec = NULL;
2048 SILC_LOG_DEBUG(("Adding new server %s", server_name));
2050 server_entry = silc_calloc(1, sizeof(*server_entry));
2054 silc_rwlock_alloc(&server_entry->internal.lock);
2055 silc_atomic_init8(&server_entry->internal.refcnt, 0);
2056 server_entry->id = *server_id;
2058 server_entry->server_name = strdup(server_name);
2060 server_entry->server_info = strdup(server_info);
2062 /* Normalize server name */
2064 server_namec = silc_identifier_check(server_name, strlen(server_name),
2065 SILC_STRING_UTF8, 256, NULL);
2066 if (!server_namec) {
2067 silc_free(server_entry->server_name);
2068 silc_free(server_entry->server_info);
2069 silc_free(server_entry);
2074 silc_mutex_lock(conn->internal->lock);
2076 /* Add server to cache */
2077 if (!silc_idcache_add(conn->internal->server_cache, server_namec,
2078 &server_entry->id, server_entry)) {
2079 silc_free(server_namec);
2080 silc_free(server_entry->server_name);
2081 silc_free(server_entry->server_info);
2082 silc_free(server_entry);
2083 silc_mutex_unlock(conn->internal->lock);
2087 silc_mutex_unlock(conn->internal->lock);
2088 silc_client_ref_server(client, conn, server_entry);
2090 SILC_LOG_DEBUG(("Added %p", server_entry));
2092 return server_entry;
2095 /* Removes server from the cache by the server entry. */
2097 SilcBool silc_client_del_server(SilcClient client, SilcClientConnection conn,
2098 SilcServerEntry server)
2100 SilcIDCacheEntry id_cache;
2101 SilcBool ret = TRUE;
2107 if (silc_atomic_sub_int8(&server->internal.refcnt, 1) > 0)
2110 SILC_LOG_DEBUG(("Deleting server %p", server));
2112 silc_mutex_lock(conn->internal->lock);
2113 if (silc_idcache_find_by_context(conn->internal->server_cache, server,
2115 namec = id_cache->name;
2116 ret = silc_idcache_del_by_context(conn->internal->server_cache,
2120 silc_mutex_unlock(conn->internal->lock);
2122 silc_free(server->server_name);
2123 silc_free(server->server_info);
2124 if (server->public_key)
2125 silc_pkcs_public_key_free(server->public_key);
2126 silc_atomic_uninit8(&server->internal.refcnt);
2127 silc_rwlock_free(server->internal.lock);
2133 /* Updates the `server_entry' with the new information sent as argument. */
2135 void silc_client_update_server(SilcClient client,
2136 SilcClientConnection conn,
2137 SilcServerEntry server_entry,
2138 const char *server_name,
2139 const char *server_info)
2141 char *server_namec = NULL;
2143 SILC_LOG_DEBUG(("Updating server %p", server_entry));
2146 (!server_entry->server_name ||
2147 !silc_utf8_strcasecmp(server_entry->server_name, server_name))) {
2149 server_namec = silc_identifier_check(server_name, strlen(server_name),
2150 SILC_STRING_UTF8, 256, NULL);
2154 silc_free(server_entry->server_name);
2155 server_entry->server_name = strdup(server_name);
2156 if (!server_entry->server_name)
2159 /* Update cache entry */
2160 silc_mutex_lock(conn->internal->lock);
2161 silc_idcache_update_by_context(conn->internal->server_cache, server_entry,
2162 NULL, server_namec, TRUE);
2163 silc_mutex_unlock(conn->internal->lock);
2167 (!server_entry->server_info ||
2168 !silc_utf8_strcasecmp(server_entry->server_info, server_info))) {
2169 silc_free(server_entry->server_info);
2170 server_entry->server_info = strdup(server_info);
2176 void silc_client_lock_server(SilcServerEntry server_entry)
2178 silc_rwlock_rdlock(server_entry->internal.lock);
2183 void silc_client_unlock_server(SilcServerEntry server_entry)
2185 silc_rwlock_unlock(server_entry->internal.lock);
2188 /* Take reference of server entry */
2190 SilcServerEntry silc_client_ref_server(SilcClient client,
2191 SilcClientConnection conn,
2192 SilcServerEntry server_entry)
2194 silc_atomic_add_int8(&server_entry->internal.refcnt, 1);
2195 SILC_LOG_DEBUG(("Server %p refcnt %d->%d", server_entry,
2196 silc_atomic_get_int8(&server_entry->internal.refcnt) - 1,
2197 silc_atomic_get_int8(&server_entry->internal.refcnt)));
2198 return server_entry;
2201 /* Release reference of server entry */
2203 void silc_client_unref_server(SilcClient client, SilcClientConnection conn,
2204 SilcServerEntry server_entry)
2207 SILC_LOG_DEBUG(("Server %p refcnt %d->%d", server_entry,
2208 silc_atomic_get_int8(&server_entry->internal.refcnt),
2209 silc_atomic_get_int8(&server_entry->internal.refcnt)
2211 silc_client_del_server(client, conn, server_entry);
2215 /* Free server entry list */
2217 void silc_client_list_free_servers(SilcClient client,
2218 SilcClientConnection conn,
2219 SilcDList server_list)
2221 SilcServerEntry server_entry;
2224 silc_dlist_start(server_list);
2225 while ((server_entry = silc_dlist_get(server_list)))
2226 silc_client_unref_server(client, conn, server_entry);
2228 silc_dlist_uninit(server_list);