5 Author: Pekka Riikonen <priikone@silcnet.org>
7 Copyright (C) 2001 - 2006 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(SilcClient client,
66 SilcClientConnection conn,
70 SilcIDCacheEntry id_cache;
73 SilcClientEntry entry;
76 if (!client || !conn || !nickname)
79 SILC_LOG_DEBUG(("Find clients by nickname %s", nickname));
81 /* Normalize nickname for search */
82 nicknamec = silc_identifier_check(nickname, strlen(nickname),
83 SILC_STRING_UTF8, 128, NULL);
87 clients = silc_dlist_init();
93 silc_mutex_lock(conn->internal->lock);
96 silc_list_init(list, struct SilcIDCacheEntryStruct, next);
97 if (!silc_idcache_find_by_name(conn->internal->client_cache, nicknamec,
99 silc_mutex_unlock(conn->internal->lock);
100 silc_free(nicknamec);
101 silc_dlist_uninit(clients);
106 /* Take all without any further checking */
107 silc_list_start(list);
108 while ((id_cache = silc_list_get(list))) {
109 silc_client_ref_client(client, conn, id_cache->context);
110 silc_dlist_add(clients, id_cache->context);
113 /* Check multiple cache entries for exact match */
114 silc_list_start(list);
115 while ((id_cache = silc_list_get(list))) {
116 entry = id_cache->context;
117 if (silc_utf8_strcasecmp(entry->nickname, format)) {
118 silc_client_ref_client(client, conn, entry);
119 silc_dlist_add(clients, entry);
124 silc_mutex_unlock(conn->internal->lock);
126 silc_dlist_start(clients);
128 silc_free(nicknamec);
132 /********************** Client Resolving from Server ************************/
134 /* Resolving context */
137 SilcGetClientCallback completion;
139 } *SilcClientGetClientInternal;
141 /* Resolving command callback */
143 static SilcBool silc_client_get_clients_cb(SilcClient client,
144 SilcClientConnection conn,
151 SilcClientGetClientInternal i = context;
152 SilcClientEntry client_entry;
154 if (error != SILC_STATUS_OK) {
155 SILC_LOG_DEBUG(("Resolving failed: %s", silc_get_status_message(error)));
157 i->completion(client, conn, error, NULL, i->context);
161 /* Add the returned client to list */
163 client_entry = va_arg(ap, SilcClientEntry);
164 silc_client_ref_client(client, conn, client_entry);
165 silc_dlist_add(i->clients, client_entry);
166 client_entry->internal.resolve_cmd_ident = 0;
169 if (status == SILC_STATUS_OK || status == SILC_STATUS_LIST_END) {
170 /* Deliver the clients to the caller */
172 SILC_LOG_DEBUG(("Resolved %d clients", silc_dlist_count(i->clients)));
173 silc_dlist_start(i->clients);
174 i->completion(client, conn, SILC_STATUS_OK, i->clients, i->context);
182 silc_client_list_free(client, conn, i->clients);
187 /* Resolves client information from server by the client ID. */
190 silc_client_get_client_by_id_resolve(SilcClient client,
191 SilcClientConnection conn,
192 SilcClientID *client_id,
193 SilcBuffer attributes,
194 SilcGetClientCallback completion,
197 SilcClientGetClientInternal i;
198 SilcClientEntry client_entry;
200 SilcUInt16 cmd_ident;
202 if (!client || !conn | !client_id)
205 SILC_LOG_DEBUG(("Resolve client by ID (%s)",
206 silc_id_render(client_id, SILC_ID_CLIENT)));
208 i = silc_calloc(1, sizeof(*i));
211 i->completion = completion;
212 i->context = context;
213 i->clients = silc_dlist_init();
219 /* Attach to resolving, if on going */
220 client_entry = silc_client_get_client_by_id(client, conn, client_id);
221 if (client_entry && client_entry->internal.resolve_cmd_ident) {
222 SILC_LOG_DEBUG(("Attach to existing resolving"));
223 silc_client_unref_client(client, conn, client_entry);
224 silc_client_command_pending(conn, SILC_COMMAND_NONE,
225 client_entry->internal.resolve_cmd_ident,
226 silc_client_get_clients_cb, i);
227 return client_entry->internal.resolve_cmd_ident;
230 /* Send the command */
231 idp = silc_id_payload_encode(client_id, SILC_ID_CLIENT);
232 cmd_ident = silc_client_command_send(client, conn, SILC_COMMAND_WHOIS,
233 silc_client_get_clients_cb, i,
234 2, 3, silc_buffer_datalen(attributes),
235 4, silc_buffer_datalen(idp));
236 if (!cmd_ident && completion)
237 completion(client, conn, SILC_STATUS_ERR_RESOURCE_LIMIT, NULL, context);
239 if (client_entry && cmd_ident)
240 client_entry->internal.resolve_cmd_ident = cmd_ident;
242 silc_client_unref_client(client, conn, client_entry);
243 silc_buffer_free(idp);
248 /* Finds client entry or entries by the `nickname' and `server'. The
249 completion callback will be called when the client entries has been
250 found. Used internally by the library. */
252 static SilcUInt16 silc_client_get_clients_i(SilcClient client,
253 SilcClientConnection conn,
255 const char *nickname,
257 SilcBuffer attributes,
258 SilcGetClientCallback completion,
261 SilcClientGetClientInternal i;
262 char userhost[768 + 1];
265 SILC_LOG_DEBUG(("Resolve client by %s command",
266 silc_get_command_name(command)));
268 if (!client || !conn)
270 if (!nickname && !attributes)
273 i = silc_calloc(1, sizeof(*i));
276 i->clients = silc_dlist_init();
281 i->completion = completion;
282 i->context = context;
284 memset(userhost, 0, sizeof(userhost));
285 if (nickname && server) {
286 len = strlen(nickname) + strlen(server) + 3;
287 silc_strncat(userhost, len, nickname, strlen(nickname));
288 silc_strncat(userhost, len, "@", 1);
289 silc_strncat(userhost, len, server, strlen(server));
290 } else if (nickname) {
291 silc_strncat(userhost, sizeof(userhost) - 1, nickname, strlen(nickname));
294 /* Send the command */
295 if (command == SILC_COMMAND_IDENTIFY)
296 return silc_client_command_send(client, conn, SILC_COMMAND_IDENTIFY,
297 silc_client_get_clients_cb, i,
298 1, 1, userhost, strlen(userhost));
299 return silc_client_command_send(client, conn, SILC_COMMAND_WHOIS,
300 silc_client_get_clients_cb, i,
301 2, 1, userhost, strlen(userhost),
302 3, silc_buffer_datalen(attributes));
305 /* Get clients from server with IDENTIFY command */
307 SilcUInt16 silc_client_get_clients(SilcClient client,
308 SilcClientConnection conn,
309 const char *nickname,
311 SilcGetClientCallback completion,
314 return silc_client_get_clients_i(client, conn, SILC_COMMAND_IDENTIFY,
315 nickname, server, NULL,
316 completion, context);
319 /* Get clients from server with WHOIS command */
321 SilcUInt16 silc_client_get_clients_whois(SilcClient client,
322 SilcClientConnection conn,
323 const char *nickname,
325 SilcBuffer attributes,
326 SilcGetClientCallback completion,
329 return silc_client_get_clients_i(client, conn, SILC_COMMAND_WHOIS,
330 nickname, server, attributes,
331 completion, context);
334 /* ID list resolving context */
336 SilcGetClientCallback completion;
338 SilcBuffer client_id_list;
339 SilcUInt32 list_count;
340 } *GetClientsByListInternal;
342 static SilcBool silc_client_get_clients_list_cb(SilcClient client,
343 SilcClientConnection conn,
350 GetClientsByListInternal i = context;
351 SilcClientEntry client_entry;
357 /* Process the list after all replies have been received */
358 if (status != SILC_STATUS_OK && !SILC_STATUS_IS_ERROR(status) &&
359 status != SILC_STATUS_LIST_END)
362 SILC_LOG_DEBUG(("Resolved all clients"));
364 clients = silc_dlist_init();
366 status = SILC_STATUS_ERR_RESOURCE_LIMIT;
370 for (c = 0; c < i->list_count; c++) {
372 SILC_GET16_MSB(idp_len, i->client_id_list->data + 2);
374 if (!silc_id_payload_parse_id(i->client_id_list->data, idp_len, &id)) {
375 status = SILC_STATUS_ERR_BAD_CLIENT_ID;
379 /* Get client entry */
380 client_entry = silc_client_get_client_by_id(client, conn, &id.u.client_id);
382 silc_dlist_add(clients, client_entry);
384 if (!silc_buffer_pull(i->client_id_list, idp_len)) {
385 status = SILC_STATUS_ERR_BAD_CLIENT_ID;
390 silc_dlist_start(clients);
391 status = SILC_STATUS_OK;
393 i->completion(client, conn, status, clients, i->context);
396 if (status != SILC_STATUS_OK && i->completion)
397 i->completion(client, conn, status, NULL, i->context);
399 silc_client_list_free(client, conn, clients);
400 silc_buffer_free(i->client_id_list);
406 /* Gets client entries by the list of client ID's `client_id_list'. This
407 always resolves those client ID's it does not know yet from the server
408 so this function might take a while. The `client_id_list' is a list
409 of ID Payloads added one after other. JOIN command reply and USERS
410 command reply for example returns this sort of list. The `completion'
411 will be called after the entries are available. */
413 SilcUInt16 silc_client_get_clients_by_list(SilcClient client,
414 SilcClientConnection conn,
415 SilcUInt32 list_count,
416 SilcBuffer client_id_list,
417 SilcGetClientCallback completion,
420 GetClientsByListInternal in;
421 SilcClientEntry entry;
422 unsigned char **res_argv = NULL;
423 SilcUInt32 *res_argv_lens = NULL, *res_argv_types = NULL, res_argc = 0;
424 SilcUInt16 idp_len, cmd_ident;
428 SILC_LOG_DEBUG(("Resolve clients from Client ID list"));
430 if (!client || !conn || !client_id_list)
433 in = silc_calloc(1, sizeof(*in));
436 in->completion = completion;
437 in->context = context;
438 in->list_count = list_count;
439 in->client_id_list = silc_buffer_copy(client_id_list);
440 if (!in->client_id_list)
443 for (i = 0; i < list_count; i++) {
445 SILC_GET16_MSB(idp_len, client_id_list->data + 2);
447 if (!silc_id_payload_parse_id(client_id_list->data, idp_len, &id))
450 /* Check if we have this client cached already. If we don't have the
451 entry or it has incomplete info, then resolve it from the server. */
452 entry = silc_client_get_client_by_id(client, conn, &id.u.client_id);
453 if (!entry || !entry->nickname[0] || !entry->username[0] ||
456 res_argv = silc_calloc(list_count, sizeof(*res_argv));
457 res_argv_lens = silc_calloc(list_count, sizeof(*res_argv_lens));
458 res_argv_types = silc_calloc(list_count, sizeof(*res_argv_types));
459 if (!res_argv || !res_argv_lens || !res_argv_types) {
460 silc_client_unref_client(client, conn, entry);
465 res_argv[res_argc] = client_id_list->data;
466 res_argv_lens[res_argc] = idp_len;
467 res_argv_types[res_argc] = res_argc + 4;
470 silc_client_unref_client(client, conn, entry);
472 if (!silc_buffer_pull(client_id_list, idp_len))
475 silc_buffer_start(client_id_list);
477 /* Query the unknown client information from server */
479 cmd_ident = silc_client_command_send_argv(client,
480 conn, SILC_COMMAND_WHOIS,
481 silc_client_get_clients_list_cb,
482 in, res_argc, res_argv,
486 silc_free(res_argv_lens);
487 silc_free(res_argv_types);
491 /* We have the clients in cache, get them and call the completion */
492 silc_client_get_clients_list_cb(client, conn, SILC_COMMAND_WHOIS,
493 SILC_STATUS_OK, SILC_STATUS_OK, in, NULL);
497 silc_buffer_free(in->client_id_list);
500 silc_free(res_argv_lens);
501 silc_free(res_argv_types);
508 SilcClientConnection conn;
509 SilcChannelID channel_id;
510 SilcGetClientCallback completion;
513 } *GetClientsByChannelInternal;
515 SILC_CLIENT_CMD_FUNC(get_clients_by_channel_cb)
517 GetClientsByChannelInternal i = context;
518 SilcClientEntry *clients = NULL;
519 SilcUInt32 clients_count = 0;
520 SilcBool found = FALSE;
521 SilcChannelEntry channel;
522 SilcHashTableList htl;
531 channel = silc_client_get_channel_by_id(i->client, i->conn, &i->channel_id);
532 if (channel && !silc_hash_table_count(channel->user_list)) {
533 clients = silc_calloc(silc_hash_table_count(channel->user_list),
535 silc_hash_table_list(channel->user_list, &htl);
536 while (silc_hash_table_get(&htl, NULL, (void *)&chu))
537 clients[clients_count++] = chu->client;
538 silc_hash_table_list_reset(&htl);
543 i->completion(i->client, i->conn, clients, clients_count, i->context);
546 i->completion(i->client, i->conn, NULL, 0, i->context);
552 /* Gets client entries by the channel entry indicated by `channel'. Thus,
553 it resolves the clients currently on that channel. */
555 void silc_client_get_clients_by_channel(SilcClient client,
556 SilcClientConnection conn,
557 SilcChannelEntry channel,
558 SilcGetClientCallback completion,
561 GetClientsByChannelInternal in;
562 SilcHashTableList htl;
564 SilcClientEntry entry;
565 unsigned char **res_argv = NULL;
566 SilcUInt32 *res_argv_lens = NULL, *res_argv_types = NULL, res_argc = 0;
568 SilcBool wait_res = FALSE;
570 assert(client && conn && channel);
572 SILC_LOG_DEBUG(("Start"));
574 in = silc_calloc(1, sizeof(*in));
577 in->channel_id = *channel->id;
578 in->completion = completion;
579 in->context = context;
581 /* If user list does not exist, send USERS command. */
582 if (!channel->user_list || !silc_hash_table_count(channel->user_list)) {
583 SILC_LOG_DEBUG(("Sending USERS"));
584 silc_client_command_register(client, SILC_COMMAND_USERS, NULL, NULL,
585 silc_client_command_reply_users_i, 0,
587 silc_client_command_send(client, conn, SILC_COMMAND_USERS,
588 conn->cmd_ident, 1, 2, channel->channel_name,
589 strlen(channel->channel_name));
590 silc_client_command_pending(conn, SILC_COMMAND_USERS, conn->cmd_ident,
591 silc_client_command_get_clients_by_channel_cb,
596 silc_hash_table_list(channel->user_list, &htl);
597 while (silc_hash_table_get(&htl, NULL, (void *)&chu)) {
600 /* If the entry has incomplete info, then resolve it from the server. */
601 if (!entry->nickname[0] || !entry->realname) {
602 if (entry->status & SILC_CLIENT_STATUS_RESOLVING) {
603 /* Attach to this resolving and wait until it finishes */
604 silc_client_command_pending(
605 conn, SILC_COMMAND_NONE,
606 entry->resolve_cmd_ident,
607 silc_client_command_get_clients_by_channel_cb,
613 entry->status |= SILC_CLIENT_STATUS_RESOLVING;
614 entry->resolve_cmd_ident = conn->cmd_ident + 1;
616 idp = silc_id_payload_encode(entry->id, SILC_ID_CLIENT);
618 /* No we don't have it, query it from the server. Assemble argument
619 table that will be sent for the WHOIS command later. */
620 res_argv = silc_realloc(res_argv, sizeof(*res_argv) *
622 res_argv_lens = silc_realloc(res_argv_lens, sizeof(*res_argv_lens) *
624 res_argv_types = silc_realloc(res_argv_types, sizeof(*res_argv_types) *
626 res_argv[res_argc] = silc_memdup(idp->data, idp->len);
627 res_argv_lens[res_argc] = idp->len;
628 res_argv_types[res_argc] = res_argc + 4;
631 silc_buffer_free(idp);
634 silc_hash_table_list_reset(&htl);
636 /* Query the client information from server if the list included clients
637 that we don't know about. */
641 /* Send the WHOIS command to server */
642 res_cmd = silc_command_payload_encode(SILC_COMMAND_WHOIS,
643 res_argc, res_argv, res_argv_lens,
644 res_argv_types, ++conn->cmd_ident);
645 silc_client_packet_send(client, conn->sock, SILC_PACKET_COMMAND,
646 NULL, 0, NULL, NULL, res_cmd->data, res_cmd->len,
649 /* Register our own command reply for this command */
650 silc_client_command_register(client, SILC_COMMAND_WHOIS, NULL, NULL,
651 silc_client_command_reply_whois_i, 0,
654 /* Process the applications request after reply has been received */
655 silc_client_command_pending(
656 conn, SILC_COMMAND_WHOIS, conn->cmd_ident,
657 silc_client_command_get_clients_by_channel_cb,
661 silc_buffer_free(res_cmd);
663 silc_free(res_argv_lens);
664 silc_free(res_argv_types);
671 /* We have the clients in cache, get them and call the completion */
672 silc_client_command_get_clients_by_channel_cb((void *)in, NULL);
677 /************************** Client Entry Routines ***************************/
679 /* Creates new client entry and adds it to the ID cache. Returns pointer
682 SilcClientEntry silc_client_add_client(SilcClient client,
683 SilcClientConnection conn,
684 char *nickname, char *username,
685 char *userinfo, SilcClientID *id,
688 SilcClientEntry client_entry;
691 SILC_LOG_DEBUG(("Adding new client entry"));
693 /* Save the client infos */
694 client_entry = silc_calloc(1, sizeof(*client_entry));
698 silc_atomic_init8(&client_entry->internal.refcnt, 0);
699 client_entry->id = *id;
700 client_entry->internal.valid = TRUE;
701 client_entry->mode = mode;
702 client_entry->realname = userinfo ? strdup(userinfo) : NULL;
703 silc_parse_userfqdn(nickname, client_entry->nickname,
704 sizeof(client_entry->nickname),
705 client_entry->server,
706 sizeof(client_entry->server));
707 silc_parse_userfqdn(username, client_entry->username,
708 sizeof(client_entry->username),
709 client_entry->hostname,
710 sizeof(client_entry->hostname));
711 client_entry->channels = silc_hash_table_alloc(1, silc_hash_ptr, NULL, NULL,
712 NULL, NULL, NULL, TRUE);
713 if (!client_entry->channels) {
714 silc_free(client_entry->realname);
715 silc_free(client_entry);
719 /* Normalize nickname */
720 if (client_entry->nickname[0]) {
721 nick = silc_identifier_check(client_entry->nickname,
722 strlen(client_entry->nickname),
723 SILC_STRING_UTF8, 128, NULL);
725 silc_free(client_entry->realname);
726 silc_hash_table_free(client_entry->channels);
727 silc_free(client_entry);
732 /* Format the nickname */
733 silc_client_nickname_format(client, conn, client_entry);
735 silc_mutex_lock(conn->internal->lock);
737 /* Add client to cache, the normalized nickname is saved to cache */
738 if (!silc_idcache_add(conn->internal->client_cache, nick,
739 &client_entry->id, client_entry)) {
741 silc_free(client_entry->realname);
742 silc_hash_table_free(client_entry->channels);
743 silc_free(client_entry);
744 silc_mutex_unlock(conn->internal->lock);
748 client_entry->nickname_normalized = nick;
750 silc_mutex_unlock(conn->internal->lock);
751 silc_client_ref_client(client, conn, client_entry);
756 /* Updates the `client_entry' with the new information sent as argument. */
758 void silc_client_update_client(SilcClient client,
759 SilcClientConnection conn,
760 SilcClientEntry client_entry,
761 const char *nickname,
762 const char *username,
763 const char *userinfo,
768 SILC_LOG_DEBUG(("Update client entry"));
770 if (!client_entry->realname && userinfo)
771 client_entry->realname = strdup(userinfo);
772 if ((!client_entry->username[0] || !client_entry->hostname[0]) && username)
773 silc_parse_userfqdn(username, client_entry->username,
774 sizeof(client_entry->username),
775 client_entry->hostname,
776 sizeof(client_entry->username));
777 if (!client_entry->nickname[0] && nickname) {
778 silc_parse_userfqdn(nickname, client_entry->nickname,
779 sizeof(client_entry->nickname),
780 client_entry->server,
781 sizeof(client_entry->server));
783 /* Normalize nickname */
784 nick = silc_identifier_check(client_entry->nickname,
785 strlen(client_entry->nickname),
786 SILC_STRING_UTF8, 128, NULL);
790 /* Format nickname */
791 silc_client_nickname_format(client, conn, client_entry);
793 /* Update cache entry */
794 silc_mutex_lock(conn->internal->lock);
795 silc_idcache_update_by_context(conn->internal->client_cache,
796 client_entry, NULL, nick, TRUE);
797 silc_mutex_unlock(conn->internal->lock);
798 client_entry->nickname_normalized = nick;
800 client_entry->mode = mode;
803 /* Deletes the client entry and frees all memory. */
805 void silc_client_del_client_entry(SilcClient client,
806 SilcClientConnection conn,
807 SilcClientEntry client_entry)
809 silc_free(client_entry->realname);
810 silc_free(client_entry->nickname_normalized);
811 silc_free(client_entry->internal.key);
812 if (client_entry->public_key)
813 silc_pkcs_public_key_free(client_entry->public_key);
814 silc_hash_table_free(client_entry->channels);
815 if (client_entry->internal.send_key)
816 silc_cipher_free(client_entry->internal.send_key);
817 if (client_entry->internal.receive_key)
818 silc_cipher_free(client_entry->internal.receive_key);
819 if (client_entry->internal.hmac_send)
820 silc_hmac_free(client_entry->internal.hmac_send);
821 if (client_entry->internal.hmac_receive)
822 silc_hmac_free(client_entry->internal.hmac_receive);
824 silc_client_ftp_session_free_client(conn, client_entry);
825 if (client_entry->internal->ke)
826 silc_client_abort_key_agreement(client, conn, client_entry);
828 silc_atomic_uninit8(&client_entry->internal.refcnt);
829 silc_free(client_entry);
832 /* Removes client from the cache by the client entry. */
834 SilcBool silc_client_del_client(SilcClient client, SilcClientConnection conn,
835 SilcClientEntry client_entry)
842 SILC_LOG_DEBUG(("Client %p refcnt %d->%d", client_entry,
843 silc_atomic_get_int8(&client_entry->internal.refcnt),
844 silc_atomic_get_int8(&client_entry->internal.refcnt) - 1));
845 if (silc_atomic_sub_int8(&client_entry->internal.refcnt, 1) > 0)
848 SILC_LOG_DEBUG(("Deleting client %p"));
850 silc_mutex_lock(conn->internal->lock);
851 ret = silc_idcache_del_by_context(conn->internal->client_cache,
853 silc_mutex_unlock(conn->internal->lock);
856 /* Remove from channels */
857 silc_client_remove_from_channels(client, conn, client_entry);
859 /* Free the client entry data */
860 silc_client_del_client_entry(client, conn, client_entry);
866 /* Take reference of client entry */
868 void silc_client_ref_client(SilcClient client, SilcClientConnection conn,
869 SilcClientEntry client_entry)
871 silc_atomic_add_int8(&client_entry->internal.refcnt, 1);
872 SILC_LOG_DEBUG(("Client %p refcnt %d->%d", client_entry,
873 silc_atomic_get_int8(&client_entry->internal.refcnt) - 1,
874 silc_atomic_get_int8(&client_entry->internal.refcnt)));
877 /* Release reference of client entry */
879 void silc_client_unref_client(SilcClient client, SilcClientConnection conn,
880 SilcClientEntry client_entry)
883 SILC_LOG_DEBUG(("Client %p refcnt %d->%d", client_entry,
884 silc_atomic_get_int8(&client_entry->internal.refcnt),
885 silc_atomic_get_int8(&client_entry->internal.refcnt) - 1));
887 silc_atomic_sub_int8(&client_entry->internal.refcnt, 1) == 0)
888 silc_client_del_client(client, conn, client_entry);
891 /* Free client entry list */
893 void silc_client_list_free(SilcClient client, SilcClientConnection conn,
894 SilcDList client_list)
896 SilcClientEntry client_entry;
899 silc_dlist_start(client_list);
900 while ((client_entry = silc_dlist_get(client_list)))
901 silc_client_unref_client(client, conn, client_entry);
903 silc_dlist_uninit(client_list);
907 /* Formats the nickname of the client specified by the `client_entry'.
908 If the format is specified by the application this will format the
909 nickname and replace the old nickname in the client entry. If the
910 format string is not specified then this function has no effect. */
912 void silc_client_nickname_format(SilcClient client,
913 SilcClientConnection conn,
914 SilcClientEntry client_entry)
917 char newnick[128 + 1];
921 SilcClientEntry entry, unformatted = NULL;
923 SILC_LOG_DEBUG(("Start"));
925 if (!client->internal->params->nickname_format[0])
928 if (!client_entry->nickname[0])
931 /* Get all clients with same nickname. Do not perform the formatting
932 if there aren't any clients with same nickname unless the application
933 is forcing us to do so. */
934 clients = silc_client_get_clients_local(client, conn,
935 client_entry->nickname, NULL);
936 if (!clients && !client->internal->params->nickname_force_format)
941 while ((entry = silc_dlist_get(clients))) {
942 if (entry->internal.valid && entry != client_entry)
944 if (entry->internal.valid && entry != client_entry &&
945 silc_utf8_strcasecmp(entry->nickname, client_entry->nickname)) {
950 if (!len || freebase)
953 /* If we are changing nickname of our local entry we'll enforce
954 that we will always get the unformatted nickname. Give our
955 format number to the one that is not formatted now. */
956 if (unformatted && client_entry == conn->local_entry)
957 client_entry = unformatted;
959 memset(newnick, 0, sizeof(newnick));
960 cp = client->internal->params->nickname_format;
970 if (!client_entry->nickname[0])
972 len = strlen(client_entry->nickname);
973 memcpy(&newnick[off], client_entry->nickname, len);
977 /* Stripped hostname */
978 if (!client_entry->hostname[0])
980 len = strcspn(client_entry->hostname, ".");
981 i = strcspn(client_entry->hostname, "-");
984 memcpy(&newnick[off], client_entry->hostname, len);
989 if (!client_entry->hostname[0])
991 len = strlen(client_entry->hostname);
992 memcpy(&newnick[off], client_entry->hostname, len);
996 /* Stripped server name */
997 if (!client_entry->server)
999 len = strcspn(client_entry->server, ".");
1000 memcpy(&newnick[off], client_entry->server, len);
1004 /* Full server name */
1005 if (!client_entry->server)
1007 len = strlen(client_entry->server);
1008 memcpy(&newnick[off], client_entry->server, len);
1012 /* Ascending number */
1017 if (silc_dlist_count(clients) == 1)
1020 silc_dlist_start(clients);
1021 while ((entry = silc_dlist_get(clients))) {
1022 if (!silc_utf8_strncasecmp(entry->nickname, newnick, off))
1024 if (strlen(entry->nickname) <= off)
1026 num = atoi(&entry->nickname[off]);
1031 memset(tmp, 0, sizeof(tmp));
1032 snprintf(tmp, sizeof(tmp) - 1, "%d", ++max);
1034 memcpy(&newnick[off], tmp, len);
1039 /* Some other character in the string */
1040 memcpy(&newnick[off], cp, 1);
1049 memcpy(client_entry->nickname, newnick, strlen(newnick));
1050 silc_client_list_free(client, conn, clients);
1053 /************************ Channel Searching Locally *************************/
1055 /* Finds entry for channel by the channel name. Returns the entry or NULL
1056 if the entry was not found. It is found only if the client is joined
1059 SilcChannelEntry silc_client_get_channel(SilcClient client,
1060 SilcClientConnection conn,
1063 SilcIDCacheEntry id_cache;
1064 SilcChannelEntry entry;
1066 if (!client || !conn || !channel)
1069 SILC_LOG_DEBUG(("Find channel %s", channel));
1071 /* Normalize name for search */
1072 channel = silc_channel_name_check(channel, strlen(channel), SILC_STRING_UTF8,
1077 silc_mutex_lock(conn->internal->lock);
1079 if (!silc_idcache_find_by_name_one(conn->internal->channel_cache, channel,
1081 silc_mutex_unlock(conn->internal->lock);
1086 SILC_LOG_DEBUG(("Found"));
1088 entry = id_cache->context;
1091 silc_client_ref_channel(client, conn, entry);
1092 silc_mutex_unlock(conn->internal->lock);
1099 /* Finds entry for channel by the channel ID. Returns the entry or NULL
1100 if the entry was not found. It is found only if the client is joined
1103 SilcChannelEntry silc_client_get_channel_by_id(SilcClient client,
1104 SilcClientConnection conn,
1105 SilcChannelID *channel_id)
1107 SilcIDCacheEntry id_cache;
1108 SilcChannelEntry entry;
1110 if (!client || !conn || !channel_id)
1113 SILC_LOG_DEBUG(("Find channel by id %s",
1114 silc_id_render(channel_id, SILC_ID_CHANNEL)));
1116 silc_mutex_lock(conn->internal->lock);
1118 if (!silc_idcache_find_by_id_one(conn->internal->channel_cache, channel_id,
1120 silc_mutex_unlock(conn->internal->lock);
1124 SILC_LOG_DEBUG(("Found"));
1126 entry = id_cache->context;
1129 silc_client_ref_channel(client, conn, entry);
1130 silc_mutex_unlock(conn->internal->lock);
1135 /********************** Channel Resolving from Server ***********************/
1137 /* Channel resolving context */
1140 SilcGetChannelCallback completion;
1142 } *SilcClientGetChannelInternal;
1144 /* Resolving command callback */
1146 static SilcBool silc_client_get_channel_cb(SilcClient client,
1147 SilcClientConnection conn,
1148 SilcCommand command,
1154 SilcClientGetChannelInternal i = context;
1155 SilcChannelEntry entry;
1157 if (error != SILC_STATUS_OK) {
1158 SILC_LOG_DEBUG(("Resolving failed: %s", silc_get_status_message(error)));
1160 i->completion(client, conn, error, NULL, i->context);
1164 /* Add the returned channel to list */
1165 if (i->completion) {
1166 entry = va_arg(ap, SilcChannelEntry);
1167 silc_client_ref_channel(client, conn, entry);
1168 silc_dlist_add(i->channels, entry);
1171 if (status == SILC_STATUS_OK || status == SILC_STATUS_LIST_END) {
1172 /* Deliver the channels to the caller */
1173 if (i->completion) {
1174 SILC_LOG_DEBUG(("Resolved %d channels", silc_dlist_count(i->channels)));
1175 silc_dlist_start(i->channels);
1176 i->completion(client, conn, SILC_STATUS_OK, i->channels, i->context);
1184 silc_client_list_free_channels(client, conn, i->channels);
1189 /* Resolves channel entry from the server by the channel name. */
1191 void silc_client_get_channel_resolve(SilcClient client,
1192 SilcClientConnection conn,
1194 SilcGetChannelCallback completion,
1197 SilcClientGetChannelInternal i;
1199 if (!client || !conn || !channel_name || !completion)
1202 SILC_LOG_DEBUG(("Resolve channel %s", channel_name));
1204 i = silc_calloc(1, sizeof(*i));
1207 i->completion = completion;
1208 i->context = context;
1209 i->channels = silc_dlist_init();
1215 /* Send the command */
1216 if (!silc_client_command_send(client, conn, SILC_COMMAND_IDENTIFY,
1217 silc_client_get_channel_cb, i, 1,
1218 3, channel_name, strlen(channel_name))) {
1220 completion(client, conn, SILC_STATUS_ERR_RESOURCE_LIMIT, NULL, context);
1224 /* Resolves channel information from the server by the channel ID. */
1227 silc_client_get_channel_by_id_resolve(SilcClient client,
1228 SilcClientConnection conn,
1229 SilcChannelID *channel_id,
1230 SilcGetChannelCallback completion,
1233 SilcClientGetChannelInternal i;
1235 SilcUInt16 cmd_ident;
1237 if (!client || !conn || !channel_id || !completion)
1240 SILC_LOG_DEBUG(("Resolve channel by id %s",
1241 silc_id_render(channel_id, SILC_ID_CHANNEL)));
1243 i = silc_calloc(1, sizeof(*i));
1246 i->completion = completion;
1247 i->context = context;
1248 i->channels = silc_dlist_init();
1254 /* Send the command */
1255 idp = silc_id_payload_encode(channel_id, SILC_ID_CHANNEL);
1256 cmd_ident = silc_client_command_send(client, conn, SILC_COMMAND_IDENTIFY,
1257 silc_client_get_channel_cb, i, 1,
1258 5, silc_buffer_datalen(idp));
1259 silc_buffer_free(idp);
1260 if (!cmd_ident && completion)
1261 completion(client, conn, SILC_STATUS_ERR_RESOURCE_LIMIT, NULL, context);
1266 /************************* Channel Entry Routines ***************************/
1268 /* Add new channel entry to the ID Cache */
1270 SilcChannelEntry silc_client_add_channel(SilcClient client,
1271 SilcClientConnection conn,
1272 const char *channel_name,
1274 SilcChannelID *channel_id)
1276 SilcChannelEntry channel;
1277 char *channel_namec;
1279 SILC_LOG_DEBUG(("Start"));
1281 channel = silc_calloc(1, sizeof(*channel));
1285 silc_atomic_init8(&channel->internal.refcnt, 0);
1286 channel->id = *channel_id;
1287 channel->mode = mode;
1289 channel->channel_name = strdup(channel_name);
1290 if (!channel->channel_name) {
1295 channel->user_list = silc_hash_table_alloc(1, silc_hash_ptr, NULL, NULL,
1296 NULL, NULL, NULL, TRUE);
1297 if (!channel->user_list) {
1298 silc_free(channel->channel_name);
1303 /* Normalize channel name */
1304 channel_namec = silc_channel_name_check(channel_name, strlen(channel_name),
1305 SILC_STRING_UTF8, 256, NULL);
1306 if (!channel_namec) {
1307 silc_free(channel->channel_name);
1308 silc_hash_table_free(channel->user_list);
1313 silc_mutex_lock(conn->internal->lock);
1315 /* Add channel to cache, the normalized channel name is saved to cache */
1316 if (!silc_idcache_add(conn->internal->channel_cache, channel_namec,
1317 &channel->id, channel)) {
1318 silc_free(channel_namec);
1319 silc_free(channel->channel_name);
1320 silc_hash_table_free(channel->user_list);
1322 silc_mutex_unlock(conn->internal->lock);
1326 silc_mutex_unlock(conn->internal->lock);
1327 silc_client_ref_channel(client, conn, channel);
1332 /* Removes channel from the cache by the channel entry. */
1334 SilcBool silc_client_del_channel(SilcClient client, SilcClientConnection conn,
1335 SilcChannelEntry channel)
1344 if (silc_atomic_sub_int8(&channel->internal.refcnt, 1) > 0)
1347 SILC_LOG_DEBUG(("Deleting channel %p", channel));
1349 silc_mutex_lock(conn->internal->lock);
1350 ret = silc_idcache_del_by_context(conn->internal->channel_cache,
1352 silc_mutex_unlock(conn->internal->lock);
1357 silc_client_empty_channel(client, conn, channel);
1358 silc_free(channel->channel_name);
1359 silc_free(channel->topic);
1360 if (channel->founder_key)
1361 silc_pkcs_public_key_free(channel->founder_key);
1362 if (channel->internal.channel_key)
1363 silc_cipher_free(channel->internal.channel_key);
1364 if (channel->internal.hmac)
1365 silc_hmac_free(channel->internal.hmac);
1366 if (channel->internal.old_channel_keys) {
1367 silc_dlist_start(channel->internal.old_channel_keys);
1368 while ((key = silc_dlist_get(channel->internal.old_channel_keys)))
1369 silc_cipher_free(key);
1370 silc_dlist_uninit(channel->internal.old_channel_keys);
1372 if (channel->internal.old_hmacs) {
1373 silc_dlist_start(channel->internal.old_hmacs);
1374 while ((hmac = silc_dlist_get(channel->internal.old_hmacs)))
1375 silc_hmac_free(hmac);
1376 silc_dlist_uninit(channel->internal.old_hmacs);
1378 silc_client_del_channel_private_keys(client, conn, channel);
1379 silc_atomic_uninit8(&channel->internal.refcnt);
1380 silc_schedule_task_del_by_context(conn->client->schedule, channel);
1386 /* Replaces the channel ID of the `channel' to `new_id'. Returns FALSE
1387 if the ID could not be changed. */
1389 SilcBool silc_client_replace_channel_id(SilcClient client,
1390 SilcClientConnection conn,
1391 SilcChannelEntry channel,
1392 SilcChannelID *new_id)
1394 SilcBool ret = FALSE;
1399 SILC_LOG_DEBUG(("Old Channel ID id(%s)",
1400 silc_id_render(&channel->id, SILC_ID_CHANNEL)));
1401 SILC_LOG_DEBUG(("New Channel ID id(%s)",
1402 silc_id_render(new_id, SILC_ID_CHANNEL)));
1405 silc_mutex_lock(conn->internal->lock);
1406 silc_idcache_update_by_context(conn->internal->channel_cache, channel,
1407 new_id, NULL, FALSE);
1408 silc_mutex_unlock(conn->internal->lock);
1413 /* Take reference of channel entry */
1415 void silc_client_ref_channel(SilcClient client, SilcClientConnection conn,
1416 SilcChannelEntry channel_entry)
1418 silc_atomic_add_int8(&channel_entry->internal.refcnt, 1);
1421 /* Release reference of channel entry */
1423 void silc_client_unref_channel(SilcClient client, SilcClientConnection conn,
1424 SilcChannelEntry channel_entry)
1426 if (channel_entry &&
1427 silc_atomic_sub_int8(&channel_entry->internal.refcnt, 1) == 0)
1428 silc_client_del_channel(client, conn, channel_entry);
1431 /* Free channel entry list */
1433 void silc_client_list_free_channels(SilcClient client,
1434 SilcClientConnection conn,
1435 SilcDList channel_list)
1437 SilcChannelEntry channel_entry;
1440 silc_dlist_start(channel_list);
1441 while ((channel_entry = silc_dlist_get(channel_list)))
1442 silc_client_unref_channel(client, conn, channel_entry);
1444 silc_dlist_uninit(channel_list);
1448 /************************* Server Searching Locally *************************/
1450 /* Finds entry for server by the server name. */
1452 SilcServerEntry silc_client_get_server(SilcClient client,
1453 SilcClientConnection conn,
1456 SilcIDCacheEntry id_cache;
1457 SilcServerEntry entry;
1459 if (!client || !conn || !server_name)
1462 SILC_LOG_DEBUG(("Find server by name %s", server_name));
1464 /* Normalize server name for search */
1465 server_name = silc_identifier_check(server_name, strlen(server_name),
1466 SILC_STRING_UTF8, 256, NULL);
1470 silc_mutex_lock(conn->internal->lock);
1472 if (!silc_idcache_find_by_name_one(conn->internal->server_cache,
1473 server_name, &id_cache)) {
1474 silc_free(server_name);
1475 silc_mutex_unlock(conn->internal->lock);
1479 SILC_LOG_DEBUG(("Found"));
1482 entry = id_cache->context;
1483 silc_client_ref_server(client, conn, entry);
1485 silc_mutex_unlock(conn->internal->lock);
1487 silc_free(server_name);
1492 /* Finds entry for server by the server ID. */
1494 SilcServerEntry silc_client_get_server_by_id(SilcClient client,
1495 SilcClientConnection conn,
1496 SilcServerID *server_id)
1498 SilcIDCacheEntry id_cache;
1499 SilcServerEntry entry;
1501 if (!client || !conn || !server_id)
1504 SILC_LOG_DEBUG(("Find server by id %s",
1505 silc_id_render(server_id, SILC_ID_SERVER)));
1507 silc_mutex_lock(conn->internal->lock);
1509 if (!silc_idcache_find_by_id_one(conn->internal->server_cache,
1510 server_id, &id_cache)) {
1511 silc_mutex_unlock(conn->internal->lock);
1515 SILC_LOG_DEBUG(("Found"));
1518 entry = id_cache->context;
1519 silc_client_ref_server(client, conn, entry);
1521 silc_mutex_unlock(conn->internal->lock);
1526 /*********************** Server Resolving from Server ***********************/
1528 /* Resolving context */
1531 SilcGetServerCallback completion;
1533 } *SilcClientGetServerInternal;
1535 /* Resolving command callback */
1537 static SilcBool silc_client_get_server_cb(SilcClient client,
1538 SilcClientConnection conn,
1539 SilcCommand command,
1545 SilcClientGetServerInternal i = context;
1546 SilcServerEntry server;
1548 if (error != SILC_STATUS_OK) {
1549 SILC_LOG_DEBUG(("Resolving failed: %s", silc_get_status_message(error)));
1551 i->completion(client, conn, error, NULL, i->context);
1555 /* Add the returned servers to list */
1556 if (i->completion) {
1557 server = va_arg(ap, SilcServerEntry);
1558 silc_client_ref_server(client, conn, server);
1559 silc_dlist_add(i->servers, server);
1560 server->internal.resolve_cmd_ident = 0;
1563 if (status == SILC_STATUS_OK || status == SILC_STATUS_LIST_END) {
1564 /* Deliver the servers to the caller */
1565 if (i->completion) {
1566 SILC_LOG_DEBUG(("Resolved %d servers", silc_dlist_count(i->servers)));
1567 silc_dlist_start(i->servers);
1568 i->completion(client, conn, SILC_STATUS_OK, i->servers, i->context);
1576 silc_client_list_free_servers(client, conn, i->servers);
1581 /* Resolve server by server ID */
1584 silc_client_get_server_by_id_resolve(SilcClient client,
1585 SilcClientConnection conn,
1586 SilcServerID *server_id,
1587 SilcGetServerCallback completion,
1590 SilcClientGetServerInternal i;
1591 SilcServerEntry server;
1593 SilcUInt16 cmd_ident;
1595 if (!client || !conn || !server_id || !completion)
1598 SILC_LOG_DEBUG(("Resolve server by id %s",
1599 silc_id_render(server_id, SILC_ID_SERVER)));
1601 i = silc_calloc(1, sizeof(*i));
1604 i->completion = completion;
1605 i->context = context;
1606 i->servers = silc_dlist_init();
1612 /* Attach to resolving, if on going */
1613 server = silc_client_get_server_by_id(client, conn, server_id);
1614 if (server && server->internal.resolve_cmd_ident) {
1615 SILC_LOG_DEBUG(("Attach to existing resolving"));
1616 silc_client_unref_server(client, conn, server);
1617 silc_client_command_pending(conn, SILC_COMMAND_NONE,
1618 server->internal.resolve_cmd_ident,
1619 silc_client_get_server_cb, i);
1620 return server->internal.resolve_cmd_ident;
1623 /* Send the command */
1624 idp = silc_id_payload_encode(server_id, SILC_ID_SERVER);
1625 cmd_ident = silc_client_command_send(client, conn, SILC_COMMAND_IDENTIFY,
1626 silc_client_get_server_cb, i, 1,
1627 5, silc_buffer_datalen(idp));
1628 silc_buffer_free(idp);
1629 if (!cmd_ident && completion)
1630 completion(client, conn, SILC_STATUS_ERR_RESOURCE_LIMIT, NULL, context);
1632 if (server && cmd_ident)
1633 server->internal.resolve_cmd_ident = cmd_ident;
1635 silc_client_unref_server(client, conn, server);
1640 /************************** Server Entry Routines ***************************/
1642 /* Add new server entry */
1644 SilcServerEntry silc_client_add_server(SilcClient client,
1645 SilcClientConnection conn,
1646 const char *server_name,
1647 const char *server_info,
1648 SilcServerID *server_id)
1650 SilcServerEntry server_entry;
1651 char *server_namec = NULL;
1656 SILC_LOG_DEBUG(("Adding new server %s", server_name));
1658 server_entry = silc_calloc(1, sizeof(*server_entry));
1662 silc_atomic_init8(&server_entry->internal.refcnt, 0);
1663 server_entry->id = *server_id;
1665 server_entry->server_name = strdup(server_name);
1667 server_entry->server_info = strdup(server_info);
1669 /* Normalize server name */
1671 server_namec = silc_identifier_check(server_name, strlen(server_name),
1672 SILC_STRING_UTF8, 256, NULL);
1673 if (!server_namec) {
1674 silc_free(server_entry->server_name);
1675 silc_free(server_entry->server_info);
1676 silc_free(server_entry);
1681 silc_mutex_lock(conn->internal->lock);
1683 /* Add server to cache */
1684 if (!silc_idcache_add(conn->internal->server_cache, server_namec,
1685 &server_entry->id, server_entry)) {
1686 silc_free(server_namec);
1687 silc_free(server_entry->server_name);
1688 silc_free(server_entry->server_info);
1689 silc_free(server_entry);
1690 silc_mutex_unlock(conn->internal->lock);
1694 silc_mutex_unlock(conn->internal->lock);
1695 silc_client_ref_server(client, conn, server_entry);
1697 return server_entry;
1700 /* Removes server from the cache by the server entry. */
1702 SilcBool silc_client_del_server(SilcClient client, SilcClientConnection conn,
1703 SilcServerEntry server)
1710 if (silc_atomic_sub_int8(&server->internal.refcnt, 1) > 0)
1713 SILC_LOG_DEBUG(("Deleting server %p", server));
1715 silc_mutex_lock(conn->internal->lock);
1716 ret = silc_idcache_del_by_context(conn->internal->server_cache,
1718 silc_mutex_unlock(conn->internal->lock);
1720 silc_free(server->server_name);
1721 silc_free(server->server_info);
1722 if (server->public_key)
1723 silc_pkcs_public_key_free(server->public_key);
1724 silc_atomic_uninit8(&server->internal.refcnt);
1730 /* Updates the `server_entry' with the new information sent as argument. */
1732 void silc_client_update_server(SilcClient client,
1733 SilcClientConnection conn,
1734 SilcServerEntry server_entry,
1735 const char *server_name,
1736 const char *server_info)
1738 char *server_namec = NULL;
1740 SILC_LOG_DEBUG(("Updating server %p", server_entry));
1743 (!server_entry->server_name ||
1744 !silc_utf8_strcasecmp(server_entry->server_name, server_name))) {
1746 server_namec = silc_identifier_check(server_name, strlen(server_name),
1747 SILC_STRING_UTF8, 256, NULL);
1751 silc_free(server_entry->server_name);
1752 server_entry->server_name = strdup(server_name);
1753 if (!server_entry->server_name)
1756 /* Update cache entry */
1757 silc_mutex_lock(conn->internal->lock);
1758 silc_idcache_update_by_context(conn->internal->server_cache, server_entry,
1759 NULL, server_namec, TRUE);
1760 silc_mutex_unlock(conn->internal->lock);
1764 (!server_entry->server_info ||
1765 !silc_utf8_strcasecmp(server_entry->server_info, server_info))) {
1766 silc_free(server_entry->server_info);
1767 server_entry->server_info = strdup(server_info);
1771 /* Take reference of server entry */
1773 void silc_client_ref_server(SilcClient client, SilcClientConnection conn,
1774 SilcServerEntry server_entry)
1776 silc_atomic_add_int8(&server_entry->internal.refcnt, 1);
1779 /* Release reference of server entry */
1781 void silc_client_unref_server(SilcClient client, SilcClientConnection conn,
1782 SilcServerEntry server_entry)
1785 silc_atomic_sub_int8(&server_entry->internal.refcnt, 1) == 0)
1786 silc_client_del_server(client, conn, server_entry);
1789 /* Free server entry list */
1791 void silc_client_list_free_servers(SilcClient client,
1792 SilcClientConnection conn,
1793 SilcDList server_list)
1795 SilcServerEntry server_entry;
1798 silc_dlist_start(server_list);
1799 while ((server_entry = silc_dlist_get(server_list)))
1800 silc_client_unref_server(client, conn, server_entry);
1802 silc_dlist_uninit(server_list);