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 + 5;
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 client_entry->id = *id;
696 client_entry->internal.valid = TRUE;
697 client_entry->mode = mode;
698 client_entry->realname = userinfo ? strdup(userinfo) : NULL;
699 silc_parse_userfqdn(nickname, client_entry->nickname,
700 sizeof(client_entry->nickname),
701 client_entry->server,
702 sizeof(client_entry->server));
703 silc_parse_userfqdn(username, client_entry->username,
704 sizeof(client_entry->username),
705 client_entry->hostname,
706 sizeof(client_entry->hostname));
707 client_entry->channels = silc_hash_table_alloc(1, silc_hash_ptr, NULL, NULL,
708 NULL, NULL, NULL, TRUE);
709 if (!client_entry->channels) {
710 silc_free(client_entry->realname);
711 silc_free(client_entry);
715 /* Normalize nickname */
716 if (client_entry->nickname[0]) {
717 nick = silc_identifier_check(client_entry->nickname,
718 strlen(client_entry->nickname),
719 SILC_STRING_UTF8, 128, NULL);
721 silc_free(client_entry->realname);
722 silc_hash_table_free(client_entry->channels);
723 silc_free(client_entry);
728 /* Format the nickname */
729 silc_client_nickname_format(client, conn, client_entry);
731 silc_mutex_lock(conn->internal->lock);
733 /* Add client to cache, the normalized nickname is saved to cache */
734 if (!silc_idcache_add(conn->internal->client_cache, nick,
735 &client_entry->id, client_entry)) {
737 silc_free(client_entry->realname);
738 silc_hash_table_free(client_entry->channels);
739 silc_free(client_entry);
740 silc_mutex_unlock(conn->internal->lock);
744 client_entry->nickname_normalized = nick;
746 silc_mutex_unlock(conn->internal->lock);
747 silc_client_ref_client(client, conn, client_entry);
752 /* Updates the `client_entry' with the new information sent as argument. */
754 void silc_client_update_client(SilcClient client,
755 SilcClientConnection conn,
756 SilcClientEntry client_entry,
757 const char *nickname,
758 const char *username,
759 const char *userinfo,
764 SILC_LOG_DEBUG(("Update client entry"));
766 if (!client_entry->realname && userinfo)
767 client_entry->realname = strdup(userinfo);
768 if ((!client_entry->username[0] || !client_entry->hostname[0]) && username)
769 silc_parse_userfqdn(username, client_entry->username,
770 sizeof(client_entry->username),
771 client_entry->hostname,
772 sizeof(client_entry->username));
773 if (!client_entry->nickname[0] && nickname) {
774 silc_parse_userfqdn(nickname, client_entry->nickname,
775 sizeof(client_entry->nickname),
776 client_entry->server,
777 sizeof(client_entry->server));
779 /* Normalize nickname */
780 nick = silc_identifier_check(client_entry->nickname,
781 strlen(client_entry->nickname),
782 SILC_STRING_UTF8, 128, NULL);
786 /* Format nickname */
787 silc_client_nickname_format(client, conn, client_entry);
789 /* Remove the old cache entry and create a new one */
790 silc_idcache_del_by_context(conn->internal->client_cache, client_entry,
792 silc_idcache_add(conn->internal->client_cache, nick, &client_entry->id,
795 client_entry->mode = mode;
798 /* Deletes the client entry and frees all memory. */
800 void silc_client_del_client_entry(SilcClient client,
801 SilcClientConnection conn,
802 SilcClientEntry client_entry)
804 SILC_LOG_DEBUG(("Start"));
806 silc_free(client_entry->realname);
807 if (client_entry->public_key)
808 silc_pkcs_public_key_free(client_entry->public_key);
809 silc_hash_table_free(client_entry->channels);
810 if (client_entry->internal.send_key)
811 silc_cipher_free(client_entry->internal.send_key);
812 if (client_entry->internal.receive_key)
813 silc_cipher_free(client_entry->internal.receive_key);
814 silc_free(client_entry->internal.key);
816 silc_client_ftp_session_free_client(conn, client_entry);
817 if (client_entry->internal->ke)
818 silc_client_abort_key_agreement(client, conn, client_entry);
820 silc_free(client_entry);
823 /* Removes client from the cache by the client entry. */
825 SilcBool silc_client_del_client(SilcClient client, SilcClientConnection conn,
826 SilcClientEntry client_entry)
828 SilcBool ret = silc_idcache_del_by_context(conn->internal->client_cache,
832 /* Remove from channels */
833 silc_client_remove_from_channels(client, conn, client_entry);
835 /* Free the client entry data */
836 silc_client_del_client_entry(client, conn, client_entry);
843 /* Take reference of client entry */
845 void silc_client_ref_client(SilcClient client, SilcClientConnection conn,
846 SilcClientEntry client_entry)
848 silc_atomic_add_int8(&client_entry->internal.refcnt, 1);
851 /* Release reference of client entry */
853 void silc_client_unref_client(SilcClient client, SilcClientConnection conn,
854 SilcClientEntry client_entry)
857 silc_atomic_sub_int8(&client_entry->internal.refcnt, 1) == 0)
858 silc_client_del_client(client, conn, client_entry);
861 /* Free client entry list */
863 void silc_client_list_free(SilcClient client, SilcClientConnection conn,
864 SilcDList client_list)
866 SilcClientEntry client_entry;
869 silc_dlist_start(client_list);
870 while ((client_entry = silc_dlist_get(client_list)))
871 silc_client_unref_client(client, conn, client_entry);
873 silc_dlist_uninit(client_list);
878 /* Formats the nickname of the client specified by the `client_entry'.
879 If the format is specified by the application this will format the
880 nickname and replace the old nickname in the client entry. If the
881 format string is not specified then this function has no effect. */
883 void silc_client_nickname_format(SilcClient client,
884 SilcClientConnection conn,
885 SilcClientEntry client_entry)
888 char newnick[128 + 1];
892 SilcClientEntry entry, unformatted = NULL;
894 SILC_LOG_DEBUG(("Start"));
896 if (!client->internal->params->nickname_format[0])
899 if (!client_entry->nickname[0])
902 /* Get all clients with same nickname. Do not perform the formatting
903 if there aren't any clients with same nickname unless the application
904 is forcing us to do so. */
905 clients = silc_client_get_clients_local(client, conn,
906 client_entry->nickname, NULL);
907 if (!clients && !client->internal->params->nickname_force_format)
912 while ((entry = silc_dlist_get(clients))) {
913 if (entry->internal.valid && entry != client_entry)
915 if (entry->internal.valid && entry != client_entry &&
916 silc_utf8_strcasecmp(entry->nickname, client_entry->nickname)) {
921 if (!len || freebase)
924 /* If we are changing nickname of our local entry we'll enforce
925 that we will always get the unformatted nickname. Give our
926 format number to the one that is not formatted now. */
927 if (unformatted && client_entry == conn->local_entry)
928 client_entry = unformatted;
930 memset(newnick, 0, sizeof(newnick));
931 cp = client->internal->params->nickname_format;
941 if (!client_entry->nickname[0])
943 len = strlen(client_entry->nickname);
944 memcpy(&newnick[off], client_entry->nickname, len);
948 /* Stripped hostname */
949 if (!client_entry->hostname[0])
951 len = strcspn(client_entry->hostname, ".");
952 i = strcspn(client_entry->hostname, "-");
955 memcpy(&newnick[off], client_entry->hostname, len);
960 if (!client_entry->hostname[0])
962 len = strlen(client_entry->hostname);
963 memcpy(&newnick[off], client_entry->hostname, len);
967 /* Stripped server name */
968 if (!client_entry->server)
970 len = strcspn(client_entry->server, ".");
971 memcpy(&newnick[off], client_entry->server, len);
975 /* Full server name */
976 if (!client_entry->server)
978 len = strlen(client_entry->server);
979 memcpy(&newnick[off], client_entry->server, len);
983 /* Ascending number */
988 if (silc_dlist_count(clients) == 1)
991 silc_dlist_start(clients);
992 while ((entry = silc_dlist_get(clients))) {
993 if (!silc_utf8_strncasecmp(entry->nickname, newnick, off))
995 if (strlen(entry->nickname) <= off)
997 num = atoi(&entry->nickname[off]);
1002 memset(tmp, 0, sizeof(tmp));
1003 snprintf(tmp, sizeof(tmp) - 1, "%d", ++max);
1005 memcpy(&newnick[off], tmp, len);
1010 /* Some other character in the string */
1011 memcpy(&newnick[off], cp, 1);
1020 memcpy(client_entry->nickname, newnick, strlen(newnick));
1021 silc_client_list_free(client, conn, clients);
1024 /************************ Channel Searching Locally *************************/
1026 /* Finds entry for channel by the channel name. Returns the entry or NULL
1027 if the entry was not found. It is found only if the client is joined
1030 SilcChannelEntry silc_client_get_channel(SilcClient client,
1031 SilcClientConnection conn,
1034 SilcIDCacheEntry id_cache;
1035 SilcChannelEntry entry;
1037 if (!client || !conn || !channel)
1040 SILC_LOG_DEBUG(("Find channel %s", channel));
1042 /* Normalize name for search */
1043 channel = silc_channel_name_check(channel, strlen(channel), SILC_STRING_UTF8,
1048 silc_mutex_lock(conn->internal->lock);
1050 if (!silc_idcache_find_by_name_one(conn->internal->channel_cache, channel,
1052 silc_mutex_unlock(conn->internal->lock);
1057 SILC_LOG_DEBUG(("Found"));
1059 entry = id_cache->context;
1062 silc_client_ref_channel(client, conn, entry);
1063 silc_mutex_unlock(conn->internal->lock);
1070 /* Finds entry for channel by the channel ID. Returns the entry or NULL
1071 if the entry was not found. It is found only if the client is joined
1074 SilcChannelEntry silc_client_get_channel_by_id(SilcClient client,
1075 SilcClientConnection conn,
1076 SilcChannelID *channel_id)
1078 SilcIDCacheEntry id_cache;
1079 SilcChannelEntry entry;
1081 if (!client || !conn || !channel_id)
1084 SILC_LOG_DEBUG(("Find channel by id %s",
1085 silc_id_render(channel_id, SILC_ID_CHANNEL)));
1087 silc_mutex_lock(conn->internal->lock);
1089 if (!silc_idcache_find_by_id_one(conn->internal->channel_cache, channel_id,
1091 silc_mutex_unlock(conn->internal->lock);
1095 SILC_LOG_DEBUG(("Found"));
1097 entry = id_cache->context;
1100 silc_client_ref_channel(client, conn, entry);
1101 silc_mutex_unlock(conn->internal->lock);
1106 /********************** Channel Resolving from Server ***********************/
1108 /* Channel resolving context */
1111 SilcGetChannelCallback completion;
1113 } *SilcClientGetChannelInternal;
1115 /* Resolving command callback */
1117 static SilcBool silc_client_get_channel_cb(SilcClient client,
1118 SilcClientConnection conn,
1119 SilcCommand command,
1125 SilcClientGetChannelInternal i = context;
1126 SilcChannelEntry entry;
1128 if (error != SILC_STATUS_OK) {
1129 SILC_LOG_DEBUG(("Resolving failed: %s", silc_get_status_message(error)));
1131 i->completion(client, conn, error, NULL, i->context);
1135 /* Add the returned channel to list */
1136 if (i->completion) {
1137 entry = va_arg(ap, SilcChannelEntry);
1138 silc_client_ref_channel(client, conn, entry);
1139 silc_dlist_add(i->channels, entry);
1142 if (status == SILC_STATUS_OK || status == SILC_STATUS_LIST_END) {
1143 /* Deliver the channels to the caller */
1144 if (i->completion) {
1145 SILC_LOG_DEBUG(("Resolved %d channels", silc_dlist_count(i->channels)));
1146 silc_dlist_start(i->channels);
1147 i->completion(client, conn, SILC_STATUS_OK, i->channels, i->context);
1155 silc_client_list_free_channels(client, conn, i->channels);
1160 /* Resolves channel entry from the server by the channel name. */
1162 void silc_client_get_channel_resolve(SilcClient client,
1163 SilcClientConnection conn,
1165 SilcGetChannelCallback completion,
1168 SilcClientGetChannelInternal i;
1170 if (!client || !conn || !channel_name || !completion)
1173 SILC_LOG_DEBUG(("Resolve channel %s", channel_name));
1175 i = silc_calloc(1, sizeof(*i));
1178 i->completion = completion;
1179 i->context = context;
1180 i->channels = silc_dlist_init();
1186 /* Send the command */
1187 if (!silc_client_command_send(client, conn, SILC_COMMAND_IDENTIFY,
1188 silc_client_get_channel_cb, i, 1,
1189 3, channel_name, strlen(channel_name))) {
1191 completion(client, conn, SILC_STATUS_ERR_RESOURCE_LIMIT, NULL, context);
1195 /* Resolves channel information from the server by the channel ID. */
1198 silc_client_get_channel_by_id_resolve(SilcClient client,
1199 SilcClientConnection conn,
1200 SilcChannelID *channel_id,
1201 SilcGetChannelCallback completion,
1204 SilcClientGetChannelInternal i;
1205 SilcChannelEntry channel;
1207 SilcUInt16 cmd_ident;
1209 if (!client || !conn || !channel_id || !completion)
1212 SILC_LOG_DEBUG(("Resolve channel by id %s",
1213 silc_id_render(channel_id, SILC_ID_CHANNEL)));
1215 i = silc_calloc(1, sizeof(*i));
1218 i->completion = completion;
1219 i->context = context;
1220 i->channels = silc_dlist_init();
1226 /* Send the command */
1227 idp = silc_id_payload_encode(channel_id, SILC_ID_CHANNEL);
1228 cmd_ident = silc_client_command_send(client, conn, SILC_COMMAND_IDENTIFY,
1229 silc_client_get_channel_cb, i, 1,
1230 5, silc_buffer_datalen(idp));
1231 silc_buffer_free(idp);
1232 if (!cmd_ident && completion)
1233 completion(client, conn, SILC_STATUS_ERR_RESOURCE_LIMIT, NULL, context);
1235 silc_client_unref_channel(client, conn, channel);
1240 /************************* Channel Entry Routines ***************************/
1242 /* Add new channel entry to the ID Cache */
1244 SilcChannelEntry silc_client_add_channel(SilcClient client,
1245 SilcClientConnection conn,
1246 const char *channel_name,
1248 SilcChannelID *channel_id)
1250 SilcChannelEntry channel;
1251 char *channel_namec;
1253 SILC_LOG_DEBUG(("Start"));
1255 channel = silc_calloc(1, sizeof(*channel));
1259 channel->id = *channel_id;
1260 channel->mode = mode;
1262 channel->channel_name = strdup(channel_name);
1263 if (!channel->channel_name) {
1268 channel->user_list = silc_hash_table_alloc(1, silc_hash_ptr, NULL, NULL,
1269 NULL, NULL, NULL, TRUE);
1270 if (!channel->user_list) {
1271 silc_free(channel->channel_name);
1276 /* Normalize channel name */
1277 channel_namec = silc_channel_name_check(channel_name, strlen(channel_name),
1278 SILC_STRING_UTF8, 256, NULL);
1279 if (!channel_namec) {
1280 silc_free(channel->channel_name);
1281 silc_hash_table_free(channel->user_list);
1286 silc_mutex_lock(conn->internal->lock);
1288 /* Add channel to cache, the normalized channel name is saved to cache */
1289 if (!silc_idcache_add(conn->internal->channel_cache, channel_namec,
1290 &channel->id, channel)) {
1291 silc_free(channel_namec);
1292 silc_free(channel->channel_name);
1293 silc_hash_table_free(channel->user_list);
1295 silc_mutex_unlock(conn->internal->lock);
1299 silc_mutex_unlock(conn->internal->lock);
1300 silc_client_ref_channel(client, conn, channel);
1305 /* Foreach callbcak to free all users from the channel when deleting a
1308 static void silc_client_del_channel_foreach(void *key, void *context,
1311 SilcChannelUser chu = (SilcChannelUser)context;
1313 SILC_LOG_DEBUG(("Start"));
1315 /* Remove the context from the client's channel hash table as that
1316 table and channel's user_list hash table share this same context. */
1317 silc_hash_table_del(chu->client->channels, chu->channel);
1321 /* Removes channel from the cache by the channel entry. */
1323 SilcBool silc_client_del_channel(SilcClient client, SilcClientConnection conn,
1324 SilcChannelEntry channel)
1326 SilcBool ret = silc_idcache_del_by_context(conn->internal->channel_cache,
1330 SILC_LOG_DEBUG(("Start"));
1332 /* Free all client entrys from the users list. The silc_hash_table_free
1333 will free all the entries so they are not freed at the foreach
1335 silc_hash_table_foreach(channel->user_list, silc_client_del_channel_foreach,
1337 silc_hash_table_free(channel->user_list);
1339 silc_free(channel->channel_name);
1340 silc_free(channel->topic);
1341 if (channel->founder_key)
1342 silc_pkcs_public_key_free(channel->founder_key);
1343 silc_free(channel->key);
1344 if (channel->channel_key)
1345 silc_cipher_free(channel->channel_key);
1347 silc_hmac_free(channel->hmac);
1348 if (channel->old_channel_keys) {
1350 silc_dlist_start(channel->old_channel_keys);
1351 while ((key = silc_dlist_get(channel->old_channel_keys)) != SILC_LIST_END)
1352 silc_cipher_free(key);
1353 silc_dlist_uninit(channel->old_channel_keys);
1355 if (channel->old_hmacs) {
1357 silc_dlist_start(channel->old_hmacs);
1358 while ((hmac = silc_dlist_get(channel->old_hmacs)) != SILC_LIST_END)
1359 silc_hmac_free(hmac);
1360 silc_dlist_uninit(channel->old_hmacs);
1362 silc_schedule_task_del_by_context(conn->client->schedule, channel);
1363 silc_client_del_channel_private_keys(client, conn, channel);
1369 /* Replaces the channel ID of the `channel' to `new_id'. Returns FALSE
1370 if the ID could not be changed. */
1372 SilcBool silc_client_replace_channel_id(SilcClient client,
1373 SilcClientConnection conn,
1374 SilcChannelEntry channel,
1375 SilcChannelID *new_id)
1377 SilcIDCacheEntry id_cache;
1378 SilcBool ret = FALSE;
1383 SILC_LOG_DEBUG(("Old Channel ID id(%s)",
1384 silc_id_render(&channel->id, SILC_ID_CHANNEL)));
1385 SILC_LOG_DEBUG(("New Channel ID id(%s)",
1386 silc_id_render(new_id, SILC_ID_CHANNEL)));
1388 silc_mutex_lock(conn->internal->lock);
1391 if (silc_idcache_find_by_id_one(conn->internal->channel_cache,
1392 &channel->id, &id_cache))
1393 ret = silc_idcache_update(conn->internal->channel_cache, id_cache,
1394 &channel->id, new_id, NULL, NULL, FALSE);
1396 silc_mutex_unlock(conn->internal->lock);
1401 /* Take reference of channel entry */
1403 void silc_client_ref_channel(SilcClient client, SilcClientConnection conn,
1404 SilcChannelEntry channel_entry)
1406 silc_atomic_add_int8(&channel_entry->internal.refcnt, 1);
1409 /* Release reference of channel entry */
1411 void silc_client_unref_channel(SilcClient client, SilcClientConnection conn,
1412 SilcChannelEntry channel_entry)
1414 if (channel_entry &&
1415 silc_atomic_sub_int8(&channel_entry->internal.refcnt, 1) == 0)
1416 silc_client_del_channel(client, conn, channel_entry);
1419 /* Free channel entry list */
1421 void silc_client_list_free_channels(SilcClient client,
1422 SilcClientConnection conn,
1423 SilcDList channel_list)
1425 SilcChannelEntry channel_entry;
1428 silc_dlist_start(channel_list);
1429 while ((channel_entry = silc_dlist_get(channel_list)))
1430 silc_client_unref_channel(client, conn, channel_entry);
1432 silc_dlist_uninit(channel_list);
1436 /************************* Server Searching Locally *************************/
1438 /* Finds entry for server by the server name. */
1440 SilcServerEntry silc_client_get_server(SilcClient client,
1441 SilcClientConnection conn,
1444 SilcIDCacheEntry id_cache;
1445 SilcServerEntry entry;
1447 if (!client || !conn || !server_name)
1450 SILC_LOG_DEBUG(("Find server by name %s", server_name));
1452 /* Normalize server name for search */
1453 server_name = silc_identifier_check(server_name, strlen(server_name),
1454 SILC_STRING_UTF8, 256, NULL);
1458 silc_mutex_lock(conn->internal->lock);
1460 if (!silc_idcache_find_by_name_one(conn->internal->server_cache,
1461 server_name, &id_cache)) {
1462 silc_free(server_name);
1466 SILC_LOG_DEBUG(("Found"));
1469 entry = id_cache->context;
1470 silc_client_ref_server(client, conn, entry);
1472 silc_mutex_unlock(conn->internal->lock);
1474 silc_free(server_name);
1479 /* Finds entry for server by the server ID. */
1481 SilcServerEntry silc_client_get_server_by_id(SilcClient client,
1482 SilcClientConnection conn,
1483 SilcServerID *server_id)
1485 SilcIDCacheEntry id_cache;
1486 SilcServerEntry entry;
1488 if (!client || !conn || !server_id)
1491 SILC_LOG_DEBUG(("Find server by id %s",
1492 silc_id_render(server_id, SILC_ID_SERVER)));
1494 silc_mutex_lock(conn->internal->lock);
1496 if (!silc_idcache_find_by_id_one(conn->internal->server_cache,
1497 server_id, &id_cache))
1500 SILC_LOG_DEBUG(("Found"));
1503 entry = id_cache->context;
1504 silc_client_ref_server(client, conn, entry);
1506 silc_mutex_unlock(conn->internal->lock);
1511 /*********************** Server Resolving from Server ***********************/
1513 /* Resolving context */
1516 SilcGetServerCallback completion;
1518 } *SilcClientGetServerInternal;
1520 /* Resolving command callback */
1522 static SilcBool silc_client_get_server_cb(SilcClient client,
1523 SilcClientConnection conn,
1524 SilcCommand command,
1530 SilcClientGetServerInternal i = context;
1531 SilcServerEntry server;
1533 if (error != SILC_STATUS_OK) {
1534 SILC_LOG_DEBUG(("Resolving failed: %s", silc_get_status_message(error)));
1536 i->completion(client, conn, error, NULL, i->context);
1540 /* Add the returned servers to list */
1541 if (i->completion) {
1542 server = va_arg(ap, SilcServerEntry);
1543 silc_client_ref_server(client, conn, server);
1544 silc_dlist_add(i->servers, server);
1545 server->internal.resolve_cmd_ident = 0;
1548 if (status == SILC_STATUS_OK || status == SILC_STATUS_LIST_END) {
1549 /* Deliver the servers to the caller */
1550 if (i->completion) {
1551 SILC_LOG_DEBUG(("Resolved %d servers", silc_dlist_count(i->servers)));
1552 silc_dlist_start(i->servers);
1553 i->completion(client, conn, SILC_STATUS_OK, i->servers, i->context);
1561 silc_client_list_free_servers(client, conn, i->servers);
1566 /* Resolve server by server ID */
1569 silc_client_get_server_by_id_resolve(SilcClient client,
1570 SilcClientConnection conn,
1571 SilcServerID *server_id,
1572 SilcGetServerCallback completion,
1575 SilcClientGetServerInternal i;
1576 SilcServerEntry server;
1578 SilcUInt16 cmd_ident;
1580 if (!client || !conn || !server_id || !completion)
1583 SILC_LOG_DEBUG(("Resolve server by id %s",
1584 silc_id_render(server_id, SILC_ID_SERVER)));
1586 i = silc_calloc(1, sizeof(*i));
1589 i->completion = completion;
1590 i->context = context;
1591 i->servers = silc_dlist_init();
1597 /* Attach to resolving, if on going */
1598 server = silc_client_get_server_by_id(client, conn, server_id);
1599 if (server && server->internal.resolve_cmd_ident) {
1600 SILC_LOG_DEBUG(("Attach to existing resolving"));
1601 silc_client_unref_server(client, conn, server);
1602 silc_client_command_pending(conn, SILC_COMMAND_NONE,
1603 server->internal.resolve_cmd_ident,
1604 silc_client_get_server_cb, i);
1605 return server->internal.resolve_cmd_ident;
1608 /* Send the command */
1609 idp = silc_id_payload_encode(server_id, SILC_ID_SERVER);
1610 cmd_ident = silc_client_command_send(client, conn, SILC_COMMAND_IDENTIFY,
1611 silc_client_get_server_cb, i, 1,
1612 5, silc_buffer_datalen(idp));
1613 silc_buffer_free(idp);
1614 if (!cmd_ident && completion)
1615 completion(client, conn, SILC_STATUS_ERR_RESOURCE_LIMIT, NULL, context);
1617 if (server && cmd_ident)
1618 server->internal.resolve_cmd_ident = cmd_ident;
1620 silc_client_unref_server(client, conn, server);
1625 /************************** Server Entry Routines ***************************/
1627 /* Add new server entry */
1629 SilcServerEntry silc_client_add_server(SilcClient client,
1630 SilcClientConnection conn,
1631 const char *server_name,
1632 const char *server_info,
1633 SilcServerID *server_id)
1635 SilcServerEntry server_entry;
1636 char *server_namec = NULL;
1638 SILC_LOG_DEBUG(("Start"));
1640 server_entry = silc_calloc(1, sizeof(*server_entry));
1641 if (!server_entry || !server_id)
1644 server_entry->id = *server_id;
1646 server_entry->server_name = strdup(server_name);
1648 server_entry->server_info = strdup(server_info);
1650 /* Normalize server name */
1652 server_namec = silc_identifier_check(server_name, strlen(server_name),
1653 SILC_STRING_UTF8, 256, NULL);
1654 if (!server_namec) {
1655 silc_free(server_entry->server_name);
1656 silc_free(server_entry->server_info);
1657 silc_free(server_entry);
1662 silc_mutex_lock(conn->internal->lock);
1664 /* Add server to cache */
1665 if (!silc_idcache_add(conn->internal->server_cache, server_namec,
1666 &server_entry->id, server_entry)) {
1667 silc_free(server_namec);
1668 silc_free(server_entry->server_name);
1669 silc_free(server_entry->server_info);
1670 silc_free(server_entry);
1671 silc_mutex_unlock(conn->internal->lock);
1675 silc_mutex_unlock(conn->internal->lock);
1676 silc_client_ref_server(client, conn, server_entry);
1678 return server_entry;
1681 /* Removes server from the cache by the server entry. */
1683 SilcBool silc_client_del_server(SilcClient client, SilcClientConnection conn,
1684 SilcServerEntry server)
1686 SilcBool ret = silc_idcache_del_by_context(conn->internal->server_cache,
1688 silc_free(server->server_name);
1689 silc_free(server->server_info);
1694 /* Updates the `server_entry' with the new information sent as argument. */
1696 void silc_client_update_server(SilcClient client,
1697 SilcClientConnection conn,
1698 SilcServerEntry server_entry,
1699 const char *server_name,
1700 const char *server_info)
1702 char *server_namec = NULL;
1704 SILC_LOG_DEBUG(("Start"));
1707 (!server_entry->server_name ||
1708 !silc_utf8_strcasecmp(server_entry->server_name, server_name))) {
1710 silc_idcache_del_by_context(conn->internal->server_cache,
1711 server_entry, NULL);
1712 silc_free(server_entry->server_name);
1713 server_entry->server_name = strdup(server_name);
1715 /* Normalize server name */
1717 server_namec = silc_identifier_check(server_name, strlen(server_name),
1718 SILC_STRING_UTF8, 256, NULL);
1722 silc_idcache_add(conn->internal->server_cache, server_namec,
1723 &server_entry->id, server_entry);
1728 (!server_entry->server_info ||
1729 !silc_utf8_strcasecmp(server_entry->server_info, server_info))) {
1730 silc_free(server_entry->server_info);
1731 server_entry->server_info = strdup(server_info);
1735 /* Take reference of server entry */
1737 void silc_client_ref_server(SilcClient client, SilcClientConnection conn,
1738 SilcServerEntry server_entry)
1740 silc_atomic_add_int8(&server_entry->internal.refcnt, 1);
1743 /* Release reference of server entry */
1745 void silc_client_unref_server(SilcClient client, SilcClientConnection conn,
1746 SilcServerEntry server_entry)
1749 silc_atomic_sub_int8(&server_entry->internal.refcnt, 1) == 0)
1750 silc_client_del_server(client, conn, server_entry);
1753 /* Free server entry list */
1755 void silc_client_list_free_servers(SilcClient client,
1756 SilcClientConnection conn,
1757 SilcDList server_list)
1759 SilcServerEntry server_entry;
1762 silc_dlist_start(server_list);
1763 while ((server_entry = silc_dlist_get(server_list)))
1764 silc_client_unref_server(client, conn, server_entry);
1766 silc_dlist_uninit(server_list);