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"
27 /************************ Client Searching Locally **************************/
29 /* Finds entry for client by the client's ID. Returns the entry or NULL
30 if the entry was not found. */
32 SilcClientEntry silc_client_get_client_by_id(SilcClient client,
33 SilcClientConnection conn,
34 SilcClientID *client_id)
36 SilcIDCacheEntry id_cache;
37 SilcClientEntry client_entry;
39 if (!client || !conn || !client_id)
42 SILC_LOG_DEBUG(("Finding client by ID (%s)",
43 silc_id_render(client_id, SILC_ID_CLIENT)));
45 silc_mutex_lock(conn->internal->lock);
47 /* Find ID from cache */
48 if (!silc_idcache_find_by_id_one(conn->internal->client_cache, client_id,
50 silc_mutex_unlock(conn->internal->lock);
54 client_entry = id_cache->context;
57 silc_client_ref_client(client, conn, client_entry);
58 silc_mutex_unlock(conn->internal->lock);
60 SILC_LOG_DEBUG(("Found"));
65 /* Finds clients by nickname from local cache. */
67 SilcDList silc_client_get_clients_local(SilcClient client,
68 SilcClientConnection conn,
72 SilcIDCacheEntry id_cache;
75 SilcClientEntry entry;
78 if (!client || !conn || !nickname)
81 /* Normalize nickname for search */
82 nicknamec = silc_identifier_check(nickname, strlen(nickname),
83 SILC_STRING_UTF8, 128, NULL);
88 clients = silc_dlist_init();
94 silc_mutex_lock(conn->internal->lock);
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, entry);
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);
398 silc_client_list_free(client, conn, clients);
403 /* Gets client entries by the list of client ID's `client_id_list'. This
404 always resolves those client ID's it does not know yet from the server
405 so this function might take a while. The `client_id_list' is a list
406 of ID Payloads added one after other. JOIN command reply and USERS
407 command reply for example returns this sort of list. The `completion'
408 will be called after the entries are available. */
410 SilcUInt16 silc_client_get_clients_by_list(SilcClient client,
411 SilcClientConnection conn,
412 SilcUInt32 list_count,
413 SilcBuffer client_id_list,
414 SilcGetClientCallback completion,
417 GetClientsByListInternal in;
418 SilcClientEntry entry;
419 unsigned char **res_argv = NULL;
420 SilcUInt32 *res_argv_lens = NULL, *res_argv_types = NULL, res_argc = 0;
421 SilcUInt16 idp_len, cmd_ident;
425 SILC_LOG_DEBUG(("Resolve clients from Client ID list"));
427 if (!client || !conn || !client_id_list)
430 in = silc_calloc(1, sizeof(*in));
433 in->completion = completion;
434 in->context = context;
435 in->list_count = list_count;
436 in->client_id_list = silc_buffer_copy(client_id_list);
437 if (!in->client_id_list)
440 for (i = 0; i < list_count; i++) {
442 SILC_GET16_MSB(idp_len, client_id_list->data + 2);
444 if (!silc_id_payload_parse_id(client_id_list->data, idp_len, &id))
447 /* Check if we have this client cached already. If we don't have the
448 entry or it has incomplete info, then resolve it from the server. */
449 entry = silc_client_get_client_by_id(client, conn, &id.u.client_id);
450 if (!entry || !entry->nickname[0] || !entry->username[0] ||
453 res_argv = silc_calloc(list_count, sizeof(*res_argv));
454 res_argv_lens = silc_calloc(list_count, sizeof(*res_argv_lens));
455 res_argv_types = silc_calloc(list_count, sizeof(*res_argv_types));
456 if (!res_argv || !res_argv_lens || !res_argv_types) {
457 silc_client_unref_client(client, conn, entry);
462 res_argv[res_argc] = client_id_list->data;
463 res_argv_lens[res_argc] = idp_len;
464 res_argv_types[res_argc] = res_argc + 4;
467 silc_client_unref_client(client, conn, entry);
469 if (!silc_buffer_pull(client_id_list, idp_len))
472 silc_buffer_start(client_id_list);
474 /* Query the unknown client information from server */
476 cmd_ident = silc_client_command_send_argv(client,
477 conn, SILC_COMMAND_WHOIS,
478 silc_client_get_clients_list_cb,
479 in, res_argc, res_argv,
483 silc_free(res_argv_lens);
484 silc_free(res_argv_types);
488 /* We have the clients in cache, get them and call the completion */
489 silc_client_get_clients_list_cb(client, conn, SILC_COMMAND_WHOIS,
490 SILC_STATUS_OK, SILC_STATUS_OK, in, NULL);
494 silc_buffer_free(in->client_id_list);
497 silc_free(res_argv_lens);
498 silc_free(res_argv_types);
505 SilcClientConnection conn;
506 SilcChannelID channel_id;
507 SilcGetClientCallback completion;
510 } *GetClientsByChannelInternal;
512 SILC_CLIENT_CMD_FUNC(get_clients_by_channel_cb)
514 GetClientsByChannelInternal i = context;
515 SilcClientEntry *clients = NULL;
516 SilcUInt32 clients_count = 0;
517 SilcBool found = FALSE;
518 SilcChannelEntry channel;
519 SilcHashTableList htl;
528 channel = silc_client_get_channel_by_id(i->client, i->conn, &i->channel_id);
529 if (channel && !silc_hash_table_count(channel->user_list)) {
530 clients = silc_calloc(silc_hash_table_count(channel->user_list),
532 silc_hash_table_list(channel->user_list, &htl);
533 while (silc_hash_table_get(&htl, NULL, (void *)&chu))
534 clients[clients_count++] = chu->client;
535 silc_hash_table_list_reset(&htl);
540 i->completion(i->client, i->conn, clients, clients_count, i->context);
543 i->completion(i->client, i->conn, NULL, 0, i->context);
549 /* Gets client entries by the channel entry indicated by `channel'. Thus,
550 it resolves the clients currently on that channel. */
552 void silc_client_get_clients_by_channel(SilcClient client,
553 SilcClientConnection conn,
554 SilcChannelEntry channel,
555 SilcGetClientCallback completion,
558 GetClientsByChannelInternal in;
559 SilcHashTableList htl;
561 SilcClientEntry entry;
562 unsigned char **res_argv = NULL;
563 SilcUInt32 *res_argv_lens = NULL, *res_argv_types = NULL, res_argc = 0;
565 SilcBool wait_res = FALSE;
567 assert(client && conn && channel);
569 SILC_LOG_DEBUG(("Start"));
571 in = silc_calloc(1, sizeof(*in));
574 in->channel_id = *channel->id;
575 in->completion = completion;
576 in->context = context;
578 /* If user list does not exist, send USERS command. */
579 if (!channel->user_list || !silc_hash_table_count(channel->user_list)) {
580 SILC_LOG_DEBUG(("Sending USERS"));
581 silc_client_command_register(client, SILC_COMMAND_USERS, NULL, NULL,
582 silc_client_command_reply_users_i, 0,
584 silc_client_command_send(client, conn, SILC_COMMAND_USERS,
585 conn->cmd_ident, 1, 2, channel->channel_name,
586 strlen(channel->channel_name));
587 silc_client_command_pending(conn, SILC_COMMAND_USERS, conn->cmd_ident,
588 silc_client_command_get_clients_by_channel_cb,
593 silc_hash_table_list(channel->user_list, &htl);
594 while (silc_hash_table_get(&htl, NULL, (void *)&chu)) {
597 /* If the entry has incomplete info, then resolve it from the server. */
598 if (!entry->nickname[0] || !entry->realname) {
599 if (entry->status & SILC_CLIENT_STATUS_RESOLVING) {
600 /* Attach to this resolving and wait until it finishes */
601 silc_client_command_pending(
602 conn, SILC_COMMAND_NONE,
603 entry->resolve_cmd_ident,
604 silc_client_command_get_clients_by_channel_cb,
610 entry->status |= SILC_CLIENT_STATUS_RESOLVING;
611 entry->resolve_cmd_ident = conn->cmd_ident + 1;
613 idp = silc_id_payload_encode(entry->id, SILC_ID_CLIENT);
615 /* No we don't have it, query it from the server. Assemble argument
616 table that will be sent for the WHOIS command later. */
617 res_argv = silc_realloc(res_argv, sizeof(*res_argv) *
619 res_argv_lens = silc_realloc(res_argv_lens, sizeof(*res_argv_lens) *
621 res_argv_types = silc_realloc(res_argv_types, sizeof(*res_argv_types) *
623 res_argv[res_argc] = silc_memdup(idp->data, idp->len);
624 res_argv_lens[res_argc] = idp->len;
625 res_argv_types[res_argc] = res_argc + 4;
628 silc_buffer_free(idp);
631 silc_hash_table_list_reset(&htl);
633 /* Query the client information from server if the list included clients
634 that we don't know about. */
638 /* Send the WHOIS command to server */
639 res_cmd = silc_command_payload_encode(SILC_COMMAND_WHOIS,
640 res_argc, res_argv, res_argv_lens,
641 res_argv_types, ++conn->cmd_ident);
642 silc_client_packet_send(client, conn->sock, SILC_PACKET_COMMAND,
643 NULL, 0, NULL, NULL, res_cmd->data, res_cmd->len,
646 /* Register our own command reply for this command */
647 silc_client_command_register(client, SILC_COMMAND_WHOIS, NULL, NULL,
648 silc_client_command_reply_whois_i, 0,
651 /* Process the applications request after reply has been received */
652 silc_client_command_pending(
653 conn, SILC_COMMAND_WHOIS, conn->cmd_ident,
654 silc_client_command_get_clients_by_channel_cb,
658 silc_buffer_free(res_cmd);
660 silc_free(res_argv_lens);
661 silc_free(res_argv_types);
668 /* We have the clients in cache, get them and call the completion */
669 silc_client_command_get_clients_by_channel_cb((void *)in, NULL);
674 /************************** Client Entry Routines ***************************/
676 /* Creates new client entry and adds it to the ID cache. Returns pointer
679 SilcClientEntry silc_client_add_client(SilcClient client,
680 SilcClientConnection conn,
681 char *nickname, char *username,
682 char *userinfo, SilcClientID *id,
685 SilcClientEntry client_entry;
688 SILC_LOG_DEBUG(("Adding new client entry"));
690 /* Save the client infos */
691 client_entry = silc_calloc(1, sizeof(*client_entry));
695 silc_atomic_init8(&client_entry->internal.refcnt, 0);
696 client_entry->id = *id;
697 client_entry->internal.valid = TRUE;
698 client_entry->mode = mode;
699 client_entry->realname = userinfo ? strdup(userinfo) : NULL;
700 silc_parse_userfqdn(nickname, client_entry->nickname,
701 sizeof(client_entry->nickname),
702 client_entry->server,
703 sizeof(client_entry->server));
704 silc_parse_userfqdn(username, client_entry->username,
705 sizeof(client_entry->username),
706 client_entry->hostname,
707 sizeof(client_entry->hostname));
708 client_entry->channels = silc_hash_table_alloc(1, silc_hash_ptr, NULL, NULL,
709 NULL, NULL, NULL, TRUE);
710 if (!client_entry->channels) {
711 silc_free(client_entry->realname);
712 silc_free(client_entry);
716 /* Normalize nickname */
717 if (client_entry->nickname[0]) {
718 nick = silc_identifier_check(client_entry->nickname,
719 strlen(client_entry->nickname),
720 SILC_STRING_UTF8, 128, NULL);
722 silc_free(client_entry->realname);
723 silc_hash_table_free(client_entry->channels);
724 silc_free(client_entry);
729 /* Format the nickname */
730 silc_client_nickname_format(client, conn, client_entry);
732 silc_mutex_lock(conn->internal->lock);
734 /* Add client to cache, the normalized nickname is saved to cache */
735 if (!silc_idcache_add(conn->internal->client_cache, nick,
736 &client_entry->id, client_entry)) {
738 silc_free(client_entry->realname);
739 silc_hash_table_free(client_entry->channels);
740 silc_free(client_entry);
741 silc_mutex_unlock(conn->internal->lock);
745 client_entry->nickname_normalized = nick;
747 silc_mutex_unlock(conn->internal->lock);
748 silc_client_ref_client(client, conn, client_entry);
753 /* Updates the `client_entry' with the new information sent as argument. */
755 void silc_client_update_client(SilcClient client,
756 SilcClientConnection conn,
757 SilcClientEntry client_entry,
758 const char *nickname,
759 const char *username,
760 const char *userinfo,
765 SILC_LOG_DEBUG(("Update client entry"));
767 if (!client_entry->realname && userinfo)
768 client_entry->realname = strdup(userinfo);
769 if ((!client_entry->username[0] || !client_entry->hostname[0]) && username)
770 silc_parse_userfqdn(username, client_entry->username,
771 sizeof(client_entry->username),
772 client_entry->hostname,
773 sizeof(client_entry->username));
774 if (!client_entry->nickname[0] && nickname) {
775 silc_parse_userfqdn(nickname, client_entry->nickname,
776 sizeof(client_entry->nickname),
777 client_entry->server,
778 sizeof(client_entry->server));
780 /* Normalize nickname */
781 nick = silc_identifier_check(client_entry->nickname,
782 strlen(client_entry->nickname),
783 SILC_STRING_UTF8, 128, NULL);
787 /* Format nickname */
788 silc_client_nickname_format(client, conn, client_entry);
790 /* Remove the old cache entry and create a new one */
791 silc_idcache_del_by_context(conn->internal->client_cache, client_entry,
793 silc_idcache_add(conn->internal->client_cache, nick, &client_entry->id,
796 client_entry->mode = mode;
799 /* Deletes the client entry and frees all memory. */
801 void silc_client_del_client_entry(SilcClient client,
802 SilcClientConnection conn,
803 SilcClientEntry client_entry)
805 SILC_LOG_DEBUG(("Start"));
807 silc_free(client_entry->realname);
808 if (client_entry->public_key)
809 silc_pkcs_public_key_free(client_entry->public_key);
810 silc_hash_table_free(client_entry->channels);
811 if (client_entry->internal.send_key)
812 silc_cipher_free(client_entry->internal.send_key);
813 if (client_entry->internal.receive_key)
814 silc_cipher_free(client_entry->internal.receive_key);
815 silc_free(client_entry->internal.key);
817 silc_client_ftp_session_free_client(conn, client_entry);
818 if (client_entry->internal->ke)
819 silc_client_abort_key_agreement(client, conn, client_entry);
821 silc_free(client_entry);
824 /* Removes client from the cache by the client entry. */
826 SilcBool silc_client_del_client(SilcClient client, SilcClientConnection conn,
827 SilcClientEntry client_entry)
829 SilcBool ret = silc_idcache_del_by_context(conn->internal->client_cache,
833 /* Remove from channels */
834 silc_client_remove_from_channels(client, conn, client_entry);
836 /* Free the client entry data */
837 silc_client_del_client_entry(client, conn, client_entry);
844 /* Take reference of client entry */
846 void silc_client_ref_client(SilcClient client, SilcClientConnection conn,
847 SilcClientEntry client_entry)
849 silc_atomic_add_int8(&client_entry->internal.refcnt, 1);
850 SILC_LOG_DEBUG(("Client %p refcnt %d->%d", client_entry,
851 silc_atomic_get_int8(&client_entry->internal.refcnt) - 1,
852 silc_atomic_get_int8(&client_entry->internal.refcnt)));
855 /* Release reference of client entry */
857 void silc_client_unref_client(SilcClient client, SilcClientConnection conn,
858 SilcClientEntry client_entry)
861 SILC_LOG_DEBUG(("Client %p refcnt %d->%d", client_entry,
862 silc_atomic_get_int8(&client_entry->internal.refcnt),
863 silc_atomic_get_int8(&client_entry->internal.refcnt) - 1));
865 silc_atomic_sub_int8(&client_entry->internal.refcnt, 1) == 0)
866 silc_client_del_client(client, conn, client_entry);
869 /* Free client entry list */
871 void silc_client_list_free(SilcClient client, SilcClientConnection conn,
872 SilcDList client_list)
874 SilcClientEntry client_entry;
877 silc_dlist_start(client_list);
878 while ((client_entry = silc_dlist_get(client_list)))
879 silc_client_unref_client(client, conn, client_entry);
881 silc_dlist_uninit(client_list);
886 /* Formats the nickname of the client specified by the `client_entry'.
887 If the format is specified by the application this will format the
888 nickname and replace the old nickname in the client entry. If the
889 format string is not specified then this function has no effect. */
891 void silc_client_nickname_format(SilcClient client,
892 SilcClientConnection conn,
893 SilcClientEntry client_entry)
896 char newnick[128 + 1];
900 SilcClientEntry entry, unformatted = NULL;
902 SILC_LOG_DEBUG(("Start"));
904 if (!client->internal->params->nickname_format[0])
907 if (!client_entry->nickname[0])
910 /* Get all clients with same nickname. Do not perform the formatting
911 if there aren't any clients with same nickname unless the application
912 is forcing us to do so. */
913 clients = silc_client_get_clients_local(client, conn,
914 client_entry->nickname, NULL);
915 if (!clients && !client->internal->params->nickname_force_format)
920 while ((entry = silc_dlist_get(clients))) {
921 if (entry->internal.valid && entry != client_entry)
923 if (entry->internal.valid && entry != client_entry &&
924 silc_utf8_strcasecmp(entry->nickname, client_entry->nickname)) {
929 if (!len || freebase)
932 /* If we are changing nickname of our local entry we'll enforce
933 that we will always get the unformatted nickname. Give our
934 format number to the one that is not formatted now. */
935 if (unformatted && client_entry == conn->local_entry)
936 client_entry = unformatted;
938 memset(newnick, 0, sizeof(newnick));
939 cp = client->internal->params->nickname_format;
949 if (!client_entry->nickname[0])
951 len = strlen(client_entry->nickname);
952 memcpy(&newnick[off], client_entry->nickname, len);
956 /* Stripped hostname */
957 if (!client_entry->hostname[0])
959 len = strcspn(client_entry->hostname, ".");
960 i = strcspn(client_entry->hostname, "-");
963 memcpy(&newnick[off], client_entry->hostname, len);
968 if (!client_entry->hostname[0])
970 len = strlen(client_entry->hostname);
971 memcpy(&newnick[off], client_entry->hostname, len);
975 /* Stripped server name */
976 if (!client_entry->server)
978 len = strcspn(client_entry->server, ".");
979 memcpy(&newnick[off], client_entry->server, len);
983 /* Full server name */
984 if (!client_entry->server)
986 len = strlen(client_entry->server);
987 memcpy(&newnick[off], client_entry->server, len);
991 /* Ascending number */
996 if (silc_dlist_count(clients) == 1)
999 silc_dlist_start(clients);
1000 while ((entry = silc_dlist_get(clients))) {
1001 if (!silc_utf8_strncasecmp(entry->nickname, newnick, off))
1003 if (strlen(entry->nickname) <= off)
1005 num = atoi(&entry->nickname[off]);
1010 memset(tmp, 0, sizeof(tmp));
1011 snprintf(tmp, sizeof(tmp) - 1, "%d", ++max);
1013 memcpy(&newnick[off], tmp, len);
1018 /* Some other character in the string */
1019 memcpy(&newnick[off], cp, 1);
1028 memcpy(client_entry->nickname, newnick, strlen(newnick));
1029 silc_client_list_free(client, conn, clients);
1032 /************************ Channel Searching Locally *************************/
1034 /* Finds entry for channel by the channel name. Returns the entry or NULL
1035 if the entry was not found. It is found only if the client is joined
1038 SilcChannelEntry silc_client_get_channel(SilcClient client,
1039 SilcClientConnection conn,
1042 SilcIDCacheEntry id_cache;
1043 SilcChannelEntry entry;
1045 if (!client || !conn || !channel)
1048 SILC_LOG_DEBUG(("Find channel %s", channel));
1050 /* Normalize name for search */
1051 channel = silc_channel_name_check(channel, strlen(channel), SILC_STRING_UTF8,
1056 silc_mutex_lock(conn->internal->lock);
1058 if (!silc_idcache_find_by_name_one(conn->internal->channel_cache, channel,
1060 silc_mutex_unlock(conn->internal->lock);
1065 SILC_LOG_DEBUG(("Found"));
1067 entry = id_cache->context;
1070 silc_client_ref_channel(client, conn, entry);
1071 silc_mutex_unlock(conn->internal->lock);
1078 /* Finds entry for channel by the channel ID. Returns the entry or NULL
1079 if the entry was not found. It is found only if the client is joined
1082 SilcChannelEntry silc_client_get_channel_by_id(SilcClient client,
1083 SilcClientConnection conn,
1084 SilcChannelID *channel_id)
1086 SilcIDCacheEntry id_cache;
1087 SilcChannelEntry entry;
1089 if (!client || !conn || !channel_id)
1092 SILC_LOG_DEBUG(("Find channel by id %s",
1093 silc_id_render(channel_id, SILC_ID_CHANNEL)));
1095 silc_mutex_lock(conn->internal->lock);
1097 if (!silc_idcache_find_by_id_one(conn->internal->channel_cache, channel_id,
1099 silc_mutex_unlock(conn->internal->lock);
1103 SILC_LOG_DEBUG(("Found"));
1105 entry = id_cache->context;
1108 silc_client_ref_channel(client, conn, entry);
1109 silc_mutex_unlock(conn->internal->lock);
1114 /********************** Channel Resolving from Server ***********************/
1116 /* Channel resolving context */
1119 SilcGetChannelCallback completion;
1121 } *SilcClientGetChannelInternal;
1123 /* Resolving command callback */
1125 static SilcBool silc_client_get_channel_cb(SilcClient client,
1126 SilcClientConnection conn,
1127 SilcCommand command,
1133 SilcClientGetChannelInternal i = context;
1134 SilcChannelEntry entry;
1136 if (error != SILC_STATUS_OK) {
1137 SILC_LOG_DEBUG(("Resolving failed: %s", silc_get_status_message(error)));
1139 i->completion(client, conn, error, NULL, i->context);
1143 /* Add the returned channel to list */
1144 if (i->completion) {
1145 entry = va_arg(ap, SilcChannelEntry);
1146 silc_client_ref_channel(client, conn, entry);
1147 silc_dlist_add(i->channels, entry);
1150 if (status == SILC_STATUS_OK || status == SILC_STATUS_LIST_END) {
1151 /* Deliver the channels to the caller */
1152 if (i->completion) {
1153 SILC_LOG_DEBUG(("Resolved %d channels", silc_dlist_count(i->channels)));
1154 silc_dlist_start(i->channels);
1155 i->completion(client, conn, SILC_STATUS_OK, i->channels, i->context);
1163 silc_client_list_free_channels(client, conn, i->channels);
1168 /* Resolves channel entry from the server by the channel name. */
1170 void silc_client_get_channel_resolve(SilcClient client,
1171 SilcClientConnection conn,
1173 SilcGetChannelCallback completion,
1176 SilcClientGetChannelInternal i;
1178 if (!client || !conn || !channel_name || !completion)
1181 SILC_LOG_DEBUG(("Resolve channel %s", channel_name));
1183 i = silc_calloc(1, sizeof(*i));
1186 i->completion = completion;
1187 i->context = context;
1188 i->channels = silc_dlist_init();
1194 /* Send the command */
1195 if (!silc_client_command_send(client, conn, SILC_COMMAND_IDENTIFY,
1196 silc_client_get_channel_cb, i, 1,
1197 3, channel_name, strlen(channel_name))) {
1199 completion(client, conn, SILC_STATUS_ERR_RESOURCE_LIMIT, NULL, context);
1203 /* Resolves channel information from the server by the channel ID. */
1206 silc_client_get_channel_by_id_resolve(SilcClient client,
1207 SilcClientConnection conn,
1208 SilcChannelID *channel_id,
1209 SilcGetChannelCallback completion,
1212 SilcClientGetChannelInternal i;
1213 SilcChannelEntry channel;
1215 SilcUInt16 cmd_ident;
1217 if (!client || !conn || !channel_id || !completion)
1220 SILC_LOG_DEBUG(("Resolve channel by id %s",
1221 silc_id_render(channel_id, SILC_ID_CHANNEL)));
1223 i = silc_calloc(1, sizeof(*i));
1226 i->completion = completion;
1227 i->context = context;
1228 i->channels = silc_dlist_init();
1234 /* Send the command */
1235 idp = silc_id_payload_encode(channel_id, SILC_ID_CHANNEL);
1236 cmd_ident = silc_client_command_send(client, conn, SILC_COMMAND_IDENTIFY,
1237 silc_client_get_channel_cb, i, 1,
1238 5, silc_buffer_datalen(idp));
1239 silc_buffer_free(idp);
1240 if (!cmd_ident && completion)
1241 completion(client, conn, SILC_STATUS_ERR_RESOURCE_LIMIT, NULL, context);
1243 silc_client_unref_channel(client, conn, channel);
1248 /************************* Channel Entry Routines ***************************/
1250 /* Add new channel entry to the ID Cache */
1252 SilcChannelEntry silc_client_add_channel(SilcClient client,
1253 SilcClientConnection conn,
1254 const char *channel_name,
1256 SilcChannelID *channel_id)
1258 SilcChannelEntry channel;
1259 char *channel_namec;
1261 SILC_LOG_DEBUG(("Start"));
1263 channel = silc_calloc(1, sizeof(*channel));
1267 silc_atomic_init8(&channel->internal.refcnt, 0);
1268 channel->id = *channel_id;
1269 channel->mode = mode;
1271 channel->channel_name = strdup(channel_name);
1272 if (!channel->channel_name) {
1277 channel->user_list = silc_hash_table_alloc(1, silc_hash_ptr, NULL, NULL,
1278 NULL, NULL, NULL, TRUE);
1279 if (!channel->user_list) {
1280 silc_free(channel->channel_name);
1285 /* Normalize channel name */
1286 channel_namec = silc_channel_name_check(channel_name, strlen(channel_name),
1287 SILC_STRING_UTF8, 256, NULL);
1288 if (!channel_namec) {
1289 silc_free(channel->channel_name);
1290 silc_hash_table_free(channel->user_list);
1295 silc_mutex_lock(conn->internal->lock);
1297 /* Add channel to cache, the normalized channel name is saved to cache */
1298 if (!silc_idcache_add(conn->internal->channel_cache, channel_namec,
1299 &channel->id, channel)) {
1300 silc_free(channel_namec);
1301 silc_free(channel->channel_name);
1302 silc_hash_table_free(channel->user_list);
1304 silc_mutex_unlock(conn->internal->lock);
1308 silc_mutex_unlock(conn->internal->lock);
1309 silc_client_ref_channel(client, conn, channel);
1314 /* Foreach callbcak to free all users from the channel when deleting a
1317 static void silc_client_del_channel_foreach(void *key, void *context,
1320 SilcChannelUser chu = (SilcChannelUser)context;
1322 SILC_LOG_DEBUG(("Start"));
1324 /* Remove the context from the client's channel hash table as that
1325 table and channel's user_list hash table share this same context. */
1326 silc_hash_table_del(chu->client->channels, chu->channel);
1330 /* Removes channel from the cache by the channel entry. */
1332 SilcBool silc_client_del_channel(SilcClient client, SilcClientConnection conn,
1333 SilcChannelEntry channel)
1335 SilcBool ret = silc_idcache_del_by_context(conn->internal->channel_cache,
1339 SILC_LOG_DEBUG(("Start"));
1341 /* Free all client entrys from the users list. The silc_hash_table_free
1342 will free all the entries so they are not freed at the foreach
1344 silc_hash_table_foreach(channel->user_list, silc_client_del_channel_foreach,
1346 silc_hash_table_free(channel->user_list);
1348 silc_free(channel->channel_name);
1349 silc_free(channel->topic);
1350 if (channel->founder_key)
1351 silc_pkcs_public_key_free(channel->founder_key);
1352 silc_free(channel->key);
1353 if (channel->channel_key)
1354 silc_cipher_free(channel->channel_key);
1356 silc_hmac_free(channel->hmac);
1357 if (channel->old_channel_keys) {
1359 silc_dlist_start(channel->old_channel_keys);
1360 while ((key = silc_dlist_get(channel->old_channel_keys)) != SILC_LIST_END)
1361 silc_cipher_free(key);
1362 silc_dlist_uninit(channel->old_channel_keys);
1364 if (channel->old_hmacs) {
1366 silc_dlist_start(channel->old_hmacs);
1367 while ((hmac = silc_dlist_get(channel->old_hmacs)) != SILC_LIST_END)
1368 silc_hmac_free(hmac);
1369 silc_dlist_uninit(channel->old_hmacs);
1371 silc_schedule_task_del_by_context(conn->client->schedule, channel);
1372 silc_client_del_channel_private_keys(client, conn, channel);
1378 /* Replaces the channel ID of the `channel' to `new_id'. Returns FALSE
1379 if the ID could not be changed. */
1381 SilcBool silc_client_replace_channel_id(SilcClient client,
1382 SilcClientConnection conn,
1383 SilcChannelEntry channel,
1384 SilcChannelID *new_id)
1386 SilcIDCacheEntry id_cache;
1387 SilcBool ret = FALSE;
1392 SILC_LOG_DEBUG(("Old Channel ID id(%s)",
1393 silc_id_render(&channel->id, SILC_ID_CHANNEL)));
1394 SILC_LOG_DEBUG(("New Channel ID id(%s)",
1395 silc_id_render(new_id, SILC_ID_CHANNEL)));
1397 silc_mutex_lock(conn->internal->lock);
1400 if (silc_idcache_find_by_id_one(conn->internal->channel_cache,
1401 &channel->id, &id_cache))
1402 ret = silc_idcache_update(conn->internal->channel_cache, id_cache,
1403 &channel->id, new_id, NULL, NULL, FALSE);
1405 silc_mutex_unlock(conn->internal->lock);
1410 /* Take reference of channel entry */
1412 void silc_client_ref_channel(SilcClient client, SilcClientConnection conn,
1413 SilcChannelEntry channel_entry)
1415 silc_atomic_add_int8(&channel_entry->internal.refcnt, 1);
1418 /* Release reference of channel entry */
1420 void silc_client_unref_channel(SilcClient client, SilcClientConnection conn,
1421 SilcChannelEntry channel_entry)
1423 if (channel_entry &&
1424 silc_atomic_sub_int8(&channel_entry->internal.refcnt, 1) == 0)
1425 silc_client_del_channel(client, conn, channel_entry);
1428 /* Free channel entry list */
1430 void silc_client_list_free_channels(SilcClient client,
1431 SilcClientConnection conn,
1432 SilcDList channel_list)
1434 SilcChannelEntry channel_entry;
1437 silc_dlist_start(channel_list);
1438 while ((channel_entry = silc_dlist_get(channel_list)))
1439 silc_client_unref_channel(client, conn, channel_entry);
1441 silc_dlist_uninit(channel_list);
1445 /************************* Server Searching Locally *************************/
1447 /* Finds entry for server by the server name. */
1449 SilcServerEntry silc_client_get_server(SilcClient client,
1450 SilcClientConnection conn,
1453 SilcIDCacheEntry id_cache;
1454 SilcServerEntry entry;
1456 if (!client || !conn || !server_name)
1459 SILC_LOG_DEBUG(("Find server by name %s", server_name));
1461 /* Normalize server name for search */
1462 server_name = silc_identifier_check(server_name, strlen(server_name),
1463 SILC_STRING_UTF8, 256, NULL);
1467 silc_mutex_lock(conn->internal->lock);
1469 if (!silc_idcache_find_by_name_one(conn->internal->server_cache,
1470 server_name, &id_cache)) {
1471 silc_free(server_name);
1475 SILC_LOG_DEBUG(("Found"));
1478 entry = id_cache->context;
1479 silc_client_ref_server(client, conn, entry);
1481 silc_mutex_unlock(conn->internal->lock);
1483 silc_free(server_name);
1488 /* Finds entry for server by the server ID. */
1490 SilcServerEntry silc_client_get_server_by_id(SilcClient client,
1491 SilcClientConnection conn,
1492 SilcServerID *server_id)
1494 SilcIDCacheEntry id_cache;
1495 SilcServerEntry entry;
1497 if (!client || !conn || !server_id)
1500 SILC_LOG_DEBUG(("Find server by id %s",
1501 silc_id_render(server_id, SILC_ID_SERVER)));
1503 silc_mutex_lock(conn->internal->lock);
1505 if (!silc_idcache_find_by_id_one(conn->internal->server_cache,
1506 server_id, &id_cache))
1509 SILC_LOG_DEBUG(("Found"));
1512 entry = id_cache->context;
1513 silc_client_ref_server(client, conn, entry);
1515 silc_mutex_unlock(conn->internal->lock);
1520 /*********************** Server Resolving from Server ***********************/
1522 /* Resolving context */
1525 SilcGetServerCallback completion;
1527 } *SilcClientGetServerInternal;
1529 /* Resolving command callback */
1531 static SilcBool silc_client_get_server_cb(SilcClient client,
1532 SilcClientConnection conn,
1533 SilcCommand command,
1539 SilcClientGetServerInternal i = context;
1540 SilcServerEntry server;
1542 if (error != SILC_STATUS_OK) {
1543 SILC_LOG_DEBUG(("Resolving failed: %s", silc_get_status_message(error)));
1545 i->completion(client, conn, error, NULL, i->context);
1549 /* Add the returned servers to list */
1550 if (i->completion) {
1551 server = va_arg(ap, SilcServerEntry);
1552 silc_client_ref_server(client, conn, server);
1553 silc_dlist_add(i->servers, server);
1554 server->internal.resolve_cmd_ident = 0;
1557 if (status == SILC_STATUS_OK || status == SILC_STATUS_LIST_END) {
1558 /* Deliver the servers to the caller */
1559 if (i->completion) {
1560 SILC_LOG_DEBUG(("Resolved %d servers", silc_dlist_count(i->servers)));
1561 silc_dlist_start(i->servers);
1562 i->completion(client, conn, SILC_STATUS_OK, i->servers, i->context);
1570 silc_client_list_free_servers(client, conn, i->servers);
1575 /* Resolve server by server ID */
1578 silc_client_get_server_by_id_resolve(SilcClient client,
1579 SilcClientConnection conn,
1580 SilcServerID *server_id,
1581 SilcGetServerCallback completion,
1584 SilcClientGetServerInternal i;
1585 SilcServerEntry server;
1587 SilcUInt16 cmd_ident;
1589 if (!client || !conn || !server_id || !completion)
1592 SILC_LOG_DEBUG(("Resolve server by id %s",
1593 silc_id_render(server_id, SILC_ID_SERVER)));
1595 i = silc_calloc(1, sizeof(*i));
1598 i->completion = completion;
1599 i->context = context;
1600 i->servers = silc_dlist_init();
1606 /* Attach to resolving, if on going */
1607 server = silc_client_get_server_by_id(client, conn, server_id);
1608 if (server && server->internal.resolve_cmd_ident) {
1609 SILC_LOG_DEBUG(("Attach to existing resolving"));
1610 silc_client_unref_server(client, conn, server);
1611 silc_client_command_pending(conn, SILC_COMMAND_NONE,
1612 server->internal.resolve_cmd_ident,
1613 silc_client_get_server_cb, i);
1614 return server->internal.resolve_cmd_ident;
1617 /* Send the command */
1618 idp = silc_id_payload_encode(server_id, SILC_ID_SERVER);
1619 cmd_ident = silc_client_command_send(client, conn, SILC_COMMAND_IDENTIFY,
1620 silc_client_get_server_cb, i, 1,
1621 5, silc_buffer_datalen(idp));
1622 silc_buffer_free(idp);
1623 if (!cmd_ident && completion)
1624 completion(client, conn, SILC_STATUS_ERR_RESOURCE_LIMIT, NULL, context);
1626 if (server && cmd_ident)
1627 server->internal.resolve_cmd_ident = cmd_ident;
1629 silc_client_unref_server(client, conn, server);
1634 /************************** Server Entry Routines ***************************/
1636 /* Add new server entry */
1638 SilcServerEntry silc_client_add_server(SilcClient client,
1639 SilcClientConnection conn,
1640 const char *server_name,
1641 const char *server_info,
1642 SilcServerID *server_id)
1644 SilcServerEntry server_entry;
1645 char *server_namec = NULL;
1647 SILC_LOG_DEBUG(("Start"));
1649 server_entry = silc_calloc(1, sizeof(*server_entry));
1650 if (!server_entry || !server_id)
1653 silc_atomic_init8(&server_entry->internal.refcnt, 0);
1654 server_entry->id = *server_id;
1656 server_entry->server_name = strdup(server_name);
1658 server_entry->server_info = strdup(server_info);
1660 /* Normalize server name */
1662 server_namec = silc_identifier_check(server_name, strlen(server_name),
1663 SILC_STRING_UTF8, 256, NULL);
1664 if (!server_namec) {
1665 silc_free(server_entry->server_name);
1666 silc_free(server_entry->server_info);
1667 silc_free(server_entry);
1672 silc_mutex_lock(conn->internal->lock);
1674 /* Add server to cache */
1675 if (!silc_idcache_add(conn->internal->server_cache, server_namec,
1676 &server_entry->id, server_entry)) {
1677 silc_free(server_namec);
1678 silc_free(server_entry->server_name);
1679 silc_free(server_entry->server_info);
1680 silc_free(server_entry);
1681 silc_mutex_unlock(conn->internal->lock);
1685 silc_mutex_unlock(conn->internal->lock);
1686 silc_client_ref_server(client, conn, server_entry);
1688 return server_entry;
1691 /* Removes server from the cache by the server entry. */
1693 SilcBool silc_client_del_server(SilcClient client, SilcClientConnection conn,
1694 SilcServerEntry server)
1696 SilcBool ret = silc_idcache_del_by_context(conn->internal->server_cache,
1698 silc_free(server->server_name);
1699 silc_free(server->server_info);
1704 /* Updates the `server_entry' with the new information sent as argument. */
1706 void silc_client_update_server(SilcClient client,
1707 SilcClientConnection conn,
1708 SilcServerEntry server_entry,
1709 const char *server_name,
1710 const char *server_info)
1712 char *server_namec = NULL;
1714 SILC_LOG_DEBUG(("Start"));
1717 (!server_entry->server_name ||
1718 !silc_utf8_strcasecmp(server_entry->server_name, server_name))) {
1720 silc_idcache_del_by_context(conn->internal->server_cache,
1721 server_entry, NULL);
1722 silc_free(server_entry->server_name);
1723 server_entry->server_name = strdup(server_name);
1725 /* Normalize server name */
1727 server_namec = silc_identifier_check(server_name, strlen(server_name),
1728 SILC_STRING_UTF8, 256, NULL);
1732 silc_idcache_add(conn->internal->server_cache, server_namec,
1733 &server_entry->id, server_entry);
1738 (!server_entry->server_info ||
1739 !silc_utf8_strcasecmp(server_entry->server_info, server_info))) {
1740 silc_free(server_entry->server_info);
1741 server_entry->server_info = strdup(server_info);
1745 /* Take reference of server entry */
1747 void silc_client_ref_server(SilcClient client, SilcClientConnection conn,
1748 SilcServerEntry server_entry)
1750 silc_atomic_add_int8(&server_entry->internal.refcnt, 1);
1753 /* Release reference of server entry */
1755 void silc_client_unref_server(SilcClient client, SilcClientConnection conn,
1756 SilcServerEntry server_entry)
1759 silc_atomic_sub_int8(&server_entry->internal.refcnt, 1) == 0)
1760 silc_client_del_server(client, conn, server_entry);
1763 /* Free server entry list */
1765 void silc_client_list_free_servers(SilcClient client,
1766 SilcClientConnection conn,
1767 SilcDList server_list)
1769 SilcServerEntry server_entry;
1772 silc_dlist_start(server_list);
1773 while ((server_entry = silc_dlist_get(server_list)))
1774 silc_client_unref_server(client, conn, server_entry);
1776 silc_dlist_uninit(server_list);