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);
751 /* Updates the `client_entry' with the new information sent as argument. */
753 void silc_client_update_client(SilcClient client,
754 SilcClientConnection conn,
755 SilcClientEntry client_entry,
756 const char *nickname,
757 const char *username,
758 const char *userinfo,
763 SILC_LOG_DEBUG(("Update client entry"));
765 if (!client_entry->realname && userinfo)
766 client_entry->realname = strdup(userinfo);
767 if ((!client_entry->username[0] || !client_entry->hostname[0]) && username)
768 silc_parse_userfqdn(username, client_entry->username,
769 sizeof(client_entry->username),
770 client_entry->hostname,
771 sizeof(client_entry->username));
772 if (!client_entry->nickname[0] && nickname) {
773 silc_parse_userfqdn(nickname, client_entry->nickname,
774 sizeof(client_entry->nickname),
775 client_entry->server,
776 sizeof(client_entry->server));
778 /* Normalize nickname */
779 nick = silc_identifier_check(client_entry->nickname,
780 strlen(client_entry->nickname),
781 SILC_STRING_UTF8, 128, NULL);
785 /* Format nickname */
786 silc_client_nickname_format(client, conn, client_entry);
788 /* Remove the old cache entry and create a new one */
789 silc_idcache_del_by_context(conn->internal->client_cache, client_entry,
791 silc_idcache_add(conn->internal->client_cache, nick, &client_entry->id,
794 client_entry->mode = mode;
797 /* Deletes the client entry and frees all memory. */
799 void silc_client_del_client_entry(SilcClient client,
800 SilcClientConnection conn,
801 SilcClientEntry client_entry)
803 SILC_LOG_DEBUG(("Start"));
805 silc_free(client_entry->realname);
806 if (client_entry->public_key)
807 silc_pkcs_public_key_free(client_entry->public_key);
808 silc_hash_table_free(client_entry->channels);
809 if (client_entry->internal.send_key)
810 silc_cipher_free(client_entry->internal.send_key);
811 if (client_entry->internal.receive_key)
812 silc_cipher_free(client_entry->internal.receive_key);
813 silc_free(client_entry->internal.key);
815 silc_client_ftp_session_free_client(conn, client_entry);
816 if (client_entry->internal->ke)
817 silc_client_abort_key_agreement(client, conn, client_entry);
819 silc_free(client_entry);
822 /* Removes client from the cache by the client entry. */
824 SilcBool silc_client_del_client(SilcClient client, SilcClientConnection conn,
825 SilcClientEntry client_entry)
827 SilcBool ret = silc_idcache_del_by_context(conn->internal->client_cache,
831 /* Remove from channels */
832 silc_client_remove_from_channels(client, conn, client_entry);
834 /* Free the client entry data */
835 silc_client_del_client_entry(client, conn, client_entry);
842 /* Take reference of client entry */
844 void silc_client_ref_client(SilcClient client, SilcClientConnection conn,
845 SilcClientEntry client_entry)
847 silc_atomic_add_int8(&client_entry->internal.refcnt, 1);
850 /* Release reference of client entry */
852 void silc_client_unref_client(SilcClient client, SilcClientConnection conn,
853 SilcClientEntry client_entry)
856 silc_atomic_sub_int8(&client_entry->internal.refcnt, 1) == 0)
857 silc_client_del_client(client, conn, client_entry);
860 /* Free client entry list */
862 void silc_client_list_free(SilcClient client, SilcClientConnection conn,
863 SilcDList client_list)
865 SilcClientEntry client_entry;
868 silc_dlist_start(client_list);
869 while ((client_entry = silc_dlist_get(client_list)))
870 silc_client_unref_client(client, conn, client_entry);
872 silc_dlist_uninit(client_list);
877 /* Formats the nickname of the client specified by the `client_entry'.
878 If the format is specified by the application this will format the
879 nickname and replace the old nickname in the client entry. If the
880 format string is not specified then this function has no effect. */
882 void silc_client_nickname_format(SilcClient client,
883 SilcClientConnection conn,
884 SilcClientEntry client_entry)
887 char newnick[128 + 1];
891 SilcClientEntry entry, unformatted = NULL;
893 SILC_LOG_DEBUG(("Start"));
895 if (!client->internal->params->nickname_format[0])
898 if (!client_entry->nickname[0])
901 /* Get all clients with same nickname. Do not perform the formatting
902 if there aren't any clients with same nickname unless the application
903 is forcing us to do so. */
904 clients = silc_client_get_clients_local(client, conn,
905 client_entry->nickname, NULL);
906 if (!clients && !client->internal->params->nickname_force_format)
911 while ((entry = silc_dlist_get(clients))) {
912 if (entry->internal.valid && entry != client_entry)
914 if (entry->internal.valid && entry != client_entry &&
915 silc_utf8_strcasecmp(entry->nickname, client_entry->nickname)) {
920 if (!len || freebase)
923 /* If we are changing nickname of our local entry we'll enforce
924 that we will always get the unformatted nickname. Give our
925 format number to the one that is not formatted now. */
926 if (unformatted && client_entry == conn->local_entry)
927 client_entry = unformatted;
929 memset(newnick, 0, sizeof(newnick));
930 cp = client->internal->params->nickname_format;
940 if (!client_entry->nickname[0])
942 len = strlen(client_entry->nickname);
943 memcpy(&newnick[off], client_entry->nickname, len);
947 /* Stripped hostname */
948 if (!client_entry->hostname[0])
950 len = strcspn(client_entry->hostname, ".");
951 i = strcspn(client_entry->hostname, "-");
954 memcpy(&newnick[off], client_entry->hostname, len);
959 if (!client_entry->hostname[0])
961 len = strlen(client_entry->hostname);
962 memcpy(&newnick[off], client_entry->hostname, len);
966 /* Stripped server name */
967 if (!client_entry->server)
969 len = strcspn(client_entry->server, ".");
970 memcpy(&newnick[off], client_entry->server, len);
974 /* Full server name */
975 if (!client_entry->server)
977 len = strlen(client_entry->server);
978 memcpy(&newnick[off], client_entry->server, len);
982 /* Ascending number */
987 if (silc_dlist_count(clients) == 1)
990 silc_dlist_start(clients);
991 while ((entry = silc_dlist_get(clients))) {
992 if (!silc_utf8_strncasecmp(entry->nickname, newnick, off))
994 if (strlen(entry->nickname) <= off)
996 num = atoi(&entry->nickname[off]);
1001 memset(tmp, 0, sizeof(tmp));
1002 snprintf(tmp, sizeof(tmp) - 1, "%d", ++max);
1004 memcpy(&newnick[off], tmp, len);
1009 /* Some other character in the string */
1010 memcpy(&newnick[off], cp, 1);
1019 memcpy(client_entry->nickname, newnick, strlen(newnick));
1020 silc_client_list_free(client, conn, clients);
1023 /************************ Channel Searching Locally *************************/
1025 /* Finds entry for channel by the channel name. Returns the entry or NULL
1026 if the entry was not found. It is found only if the client is joined
1029 SilcChannelEntry silc_client_get_channel(SilcClient client,
1030 SilcClientConnection conn,
1033 SilcIDCacheEntry id_cache;
1034 SilcChannelEntry entry;
1036 if (!client || !conn || !channel)
1039 SILC_LOG_DEBUG(("Find channel %s", channel));
1041 /* Normalize name for search */
1042 channel = silc_channel_name_check(channel, strlen(channel), SILC_STRING_UTF8,
1047 silc_mutex_lock(conn->internal->lock);
1049 if (!silc_idcache_find_by_name_one(conn->internal->channel_cache, channel,
1051 silc_mutex_unlock(conn->internal->lock);
1056 SILC_LOG_DEBUG(("Found"));
1058 entry = id_cache->context;
1061 silc_client_ref_channel(client, conn, entry);
1062 silc_mutex_unlock(conn->internal->lock);
1069 /* Finds entry for channel by the channel ID. Returns the entry or NULL
1070 if the entry was not found. It is found only if the client is joined
1073 SilcChannelEntry silc_client_get_channel_by_id(SilcClient client,
1074 SilcClientConnection conn,
1075 SilcChannelID *channel_id)
1077 SilcIDCacheEntry id_cache;
1078 SilcChannelEntry entry;
1080 if (!client || !conn || !channel_id)
1083 SILC_LOG_DEBUG(("Find channel by id %s",
1084 silc_id_render(channel_id, SILC_ID_CHANNEL)));
1086 silc_mutex_lock(conn->internal->lock);
1088 if (!silc_idcache_find_by_id_one(conn->internal->channel_cache, channel_id,
1090 silc_mutex_unlock(conn->internal->lock);
1094 SILC_LOG_DEBUG(("Found"));
1096 entry = id_cache->context;
1099 silc_client_ref_channel(client, conn, entry);
1100 silc_mutex_unlock(conn->internal->lock);
1105 /********************** Channel Resolving from Server ***********************/
1107 /* Channel resolving context */
1110 SilcGetChannelCallback completion;
1112 } *SilcClientGetChannelInternal;
1114 /* Resolving command callback */
1116 static SilcBool silc_client_get_channel_cb(SilcClient client,
1117 SilcClientConnection conn,
1118 SilcCommand command,
1124 SilcClientGetChannelInternal i = context;
1125 SilcChannelEntry entry;
1127 if (error != SILC_STATUS_OK) {
1128 SILC_LOG_DEBUG(("Resolving failed: %s", silc_get_status_message(error)));
1130 i->completion(client, conn, error, NULL, i->context);
1134 /* Add the returned channel to list */
1135 if (i->completion) {
1136 entry = va_arg(ap, SilcChannelEntry);
1137 silc_client_ref_channel(client, conn, entry);
1138 silc_dlist_add(i->channels, entry);
1141 if (status == SILC_STATUS_OK || status == SILC_STATUS_LIST_END) {
1142 /* Deliver the channels to the caller */
1143 if (i->completion) {
1144 SILC_LOG_DEBUG(("Resolved %d channels", silc_dlist_count(i->channels)));
1145 silc_dlist_start(i->channels);
1146 i->completion(client, conn, SILC_STATUS_OK, i->channels, i->context);
1154 silc_client_list_free_channels(client, conn, i->channels);
1159 /* Resolves channel entry from the server by the channel name. */
1161 void silc_client_get_channel_resolve(SilcClient client,
1162 SilcClientConnection conn,
1164 SilcGetChannelCallback completion,
1167 SilcClientGetChannelInternal i;
1169 if (!client || !conn || !channel_name || !completion)
1172 SILC_LOG_DEBUG(("Resolve channel %s", channel_name));
1174 i = silc_calloc(1, sizeof(*i));
1177 i->completion = completion;
1178 i->context = context;
1179 i->channels = silc_dlist_init();
1185 /* Send the command */
1186 if (!silc_client_command_send(client, conn, SILC_COMMAND_IDENTIFY,
1187 silc_client_get_channel_cb, i, 1,
1188 3, channel_name, strlen(channel_name))) {
1190 completion(client, conn, SILC_STATUS_ERR_RESOURCE_LIMIT, NULL, context);
1194 /* Resolves channel information from the server by the channel ID. */
1197 silc_client_get_channel_by_id_resolve(SilcClient client,
1198 SilcClientConnection conn,
1199 SilcChannelID *channel_id,
1200 SilcGetChannelCallback completion,
1203 SilcClientGetChannelInternal i;
1204 SilcChannelEntry channel;
1206 SilcUInt16 cmd_ident;
1208 if (!client || !conn || !channel_id || !completion)
1211 SILC_LOG_DEBUG(("Resolve channel by id %s",
1212 silc_id_render(channel_id, SILC_ID_CHANNEL)));
1214 i = silc_calloc(1, sizeof(*i));
1217 i->completion = completion;
1218 i->context = context;
1219 i->channels = silc_dlist_init();
1225 /* Send the command */
1226 idp = silc_id_payload_encode(channel_id, SILC_ID_CHANNEL);
1227 cmd_ident = silc_client_command_send(client, conn, SILC_COMMAND_IDENTIFY,
1228 silc_client_get_channel_cb, i, 1,
1229 5, silc_buffer_datalen(idp));
1230 silc_buffer_free(idp);
1231 if (!cmd_ident && completion)
1232 completion(client, conn, SILC_STATUS_ERR_RESOURCE_LIMIT, NULL, context);
1234 silc_client_unref_channel(client, conn, channel);
1239 /************************* Channel Entry Routines ***************************/
1241 /* Add new channel entry to the ID Cache */
1243 SilcChannelEntry silc_client_add_channel(SilcClient client,
1244 SilcClientConnection conn,
1245 const char *channel_name,
1247 SilcChannelID *channel_id)
1249 SilcChannelEntry channel;
1250 char *channel_namec;
1252 SILC_LOG_DEBUG(("Start"));
1254 channel = silc_calloc(1, sizeof(*channel));
1258 channel->id = *channel_id;
1259 channel->mode = mode;
1261 channel->channel_name = strdup(channel_name);
1262 if (!channel->channel_name) {
1267 channel->user_list = silc_hash_table_alloc(1, silc_hash_ptr, NULL, NULL,
1268 NULL, NULL, NULL, TRUE);
1269 if (!channel->user_list) {
1270 silc_free(channel->channel_name);
1275 /* Normalize channel name */
1276 channel_namec = silc_channel_name_check(channel_name, strlen(channel_name),
1277 SILC_STRING_UTF8, 256, NULL);
1278 if (!channel_namec) {
1279 silc_free(channel->channel_name);
1280 silc_hash_table_free(channel->user_list);
1285 silc_mutex_lock(conn->internal->lock);
1287 /* Add channel to cache, the normalized channel name is saved to cache */
1288 if (!silc_idcache_add(conn->internal->channel_cache, channel_namec,
1289 &channel->id, channel)) {
1290 silc_free(channel_namec);
1291 silc_free(channel->channel_name);
1292 silc_hash_table_free(channel->user_list);
1294 silc_mutex_unlock(conn->internal->lock);
1298 silc_mutex_unlock(conn->internal->lock);
1303 /* Foreach callbcak to free all users from the channel when deleting a
1306 static void silc_client_del_channel_foreach(void *key, void *context,
1309 SilcChannelUser chu = (SilcChannelUser)context;
1311 SILC_LOG_DEBUG(("Start"));
1313 /* Remove the context from the client's channel hash table as that
1314 table and channel's user_list hash table share this same context. */
1315 silc_hash_table_del(chu->client->channels, chu->channel);
1319 /* Removes channel from the cache by the channel entry. */
1321 SilcBool silc_client_del_channel(SilcClient client, SilcClientConnection conn,
1322 SilcChannelEntry channel)
1324 SilcBool ret = silc_idcache_del_by_context(conn->internal->channel_cache,
1328 SILC_LOG_DEBUG(("Start"));
1330 /* Free all client entrys from the users list. The silc_hash_table_free
1331 will free all the entries so they are not freed at the foreach
1333 silc_hash_table_foreach(channel->user_list, silc_client_del_channel_foreach,
1335 silc_hash_table_free(channel->user_list);
1337 silc_free(channel->channel_name);
1338 silc_free(channel->topic);
1339 if (channel->founder_key)
1340 silc_pkcs_public_key_free(channel->founder_key);
1341 silc_free(channel->key);
1342 if (channel->channel_key)
1343 silc_cipher_free(channel->channel_key);
1345 silc_hmac_free(channel->hmac);
1346 if (channel->old_channel_keys) {
1348 silc_dlist_start(channel->old_channel_keys);
1349 while ((key = silc_dlist_get(channel->old_channel_keys)) != SILC_LIST_END)
1350 silc_cipher_free(key);
1351 silc_dlist_uninit(channel->old_channel_keys);
1353 if (channel->old_hmacs) {
1355 silc_dlist_start(channel->old_hmacs);
1356 while ((hmac = silc_dlist_get(channel->old_hmacs)) != SILC_LIST_END)
1357 silc_hmac_free(hmac);
1358 silc_dlist_uninit(channel->old_hmacs);
1360 silc_schedule_task_del_by_context(conn->client->schedule, channel);
1361 silc_client_del_channel_private_keys(client, conn, channel);
1367 /* Replaces the channel ID of the `channel' to `new_id'. Returns FALSE
1368 if the ID could not be changed. */
1370 SilcBool silc_client_replace_channel_id(SilcClient client,
1371 SilcClientConnection conn,
1372 SilcChannelEntry channel,
1373 SilcChannelID *new_id)
1375 SilcIDCacheEntry id_cache;
1376 SilcBool ret = FALSE;
1381 SILC_LOG_DEBUG(("Old Channel ID id(%s)",
1382 silc_id_render(&channel->id, SILC_ID_CHANNEL)));
1383 SILC_LOG_DEBUG(("New Channel ID id(%s)",
1384 silc_id_render(new_id, SILC_ID_CHANNEL)));
1386 silc_mutex_lock(conn->internal->lock);
1389 if (silc_idcache_find_by_id_one(conn->internal->channel_cache,
1390 &channel->id, &id_cache))
1391 ret = silc_idcache_update(conn->internal->channel_cache, id_cache,
1392 &channel->id, new_id, NULL, NULL, FALSE);
1394 silc_mutex_unlock(conn->internal->lock);
1399 /* Take reference of channel entry */
1401 void silc_client_ref_channel(SilcClient client, SilcClientConnection conn,
1402 SilcChannelEntry channel_entry)
1404 silc_atomic_add_int8(&channel_entry->internal.refcnt, 1);
1407 /* Release reference of channel entry */
1409 void silc_client_unref_channel(SilcClient client, SilcClientConnection conn,
1410 SilcChannelEntry channel_entry)
1412 if (channel_entry &&
1413 silc_atomic_sub_int8(&channel_entry->internal.refcnt, 1) == 0)
1414 silc_client_del_channel(client, conn, channel_entry);
1417 /* Free channel entry list */
1419 void silc_client_list_free_channels(SilcClient client,
1420 SilcClientConnection conn,
1421 SilcDList channel_list)
1423 SilcChannelEntry channel_entry;
1426 silc_dlist_start(channel_list);
1427 while ((channel_entry = silc_dlist_get(channel_list)))
1428 silc_client_unref_channel(client, conn, channel_entry);
1430 silc_dlist_uninit(channel_list);
1434 /************************* Server Searching Locally *************************/
1436 /* Finds entry for server by the server name. */
1438 SilcServerEntry silc_client_get_server(SilcClient client,
1439 SilcClientConnection conn,
1442 SilcIDCacheEntry id_cache;
1443 SilcServerEntry entry;
1445 if (!client || !conn || !server_name)
1448 SILC_LOG_DEBUG(("Find server by name %s", server_name));
1450 /* Normalize server name for search */
1451 server_name = silc_identifier_check(server_name, strlen(server_name),
1452 SILC_STRING_UTF8, 256, NULL);
1456 silc_mutex_lock(conn->internal->lock);
1458 if (!silc_idcache_find_by_name_one(conn->internal->server_cache,
1459 server_name, &id_cache)) {
1460 silc_free(server_name);
1464 SILC_LOG_DEBUG(("Found"));
1467 entry = id_cache->context;
1468 silc_client_ref_server(client, conn, entry);
1470 silc_mutex_unlock(conn->internal->lock);
1472 silc_free(server_name);
1477 /* Finds entry for server by the server ID. */
1479 SilcServerEntry silc_client_get_server_by_id(SilcClient client,
1480 SilcClientConnection conn,
1481 SilcServerID *server_id)
1483 SilcIDCacheEntry id_cache;
1484 SilcServerEntry entry;
1486 if (!client || !conn || !server_id)
1489 SILC_LOG_DEBUG(("Find server by id %s",
1490 silc_id_render(server_id, SILC_ID_SERVER)));
1492 silc_mutex_lock(conn->internal->lock);
1494 if (!silc_idcache_find_by_id_one(conn->internal->server_cache,
1495 server_id, &id_cache))
1498 SILC_LOG_DEBUG(("Found"));
1501 entry = id_cache->context;
1502 silc_client_ref_server(client, conn, entry);
1504 silc_mutex_unlock(conn->internal->lock);
1509 /*********************** Server Resolving from Server ***********************/
1511 /* Resolving context */
1514 SilcGetServerCallback completion;
1516 } *SilcClientGetServerInternal;
1518 /* Resolving command callback */
1520 static SilcBool silc_client_get_server_cb(SilcClient client,
1521 SilcClientConnection conn,
1522 SilcCommand command,
1528 SilcClientGetServerInternal i = context;
1529 SilcServerEntry server;
1531 if (error != SILC_STATUS_OK) {
1532 SILC_LOG_DEBUG(("Resolving failed: %s", silc_get_status_message(error)));
1534 i->completion(client, conn, error, NULL, i->context);
1538 /* Add the returned servers to list */
1539 if (i->completion) {
1540 server = va_arg(ap, SilcServerEntry);
1541 silc_client_ref_server(client, conn, server);
1542 silc_dlist_add(i->servers, server);
1543 server->internal.resolve_cmd_ident = 0;
1546 if (status == SILC_STATUS_OK || status == SILC_STATUS_LIST_END) {
1547 /* Deliver the servers to the caller */
1548 if (i->completion) {
1549 SILC_LOG_DEBUG(("Resolved %d servers", silc_dlist_count(i->servers)));
1550 silc_dlist_start(i->servers);
1551 i->completion(client, conn, SILC_STATUS_OK, i->servers, i->context);
1559 silc_client_list_free_servers(client, conn, i->servers);
1564 /* Resolve server by server ID */
1567 silc_client_get_server_by_id_resolve(SilcClient client,
1568 SilcClientConnection conn,
1569 SilcServerID *server_id,
1570 SilcGetServerCallback completion,
1573 SilcClientGetServerInternal i;
1574 SilcServerEntry server;
1576 SilcUInt16 cmd_ident;
1578 if (!client || !conn || !server_id || !completion)
1581 SILC_LOG_DEBUG(("Resolve server by id %s",
1582 silc_id_render(server_id, SILC_ID_SERVER)));
1584 i = silc_calloc(1, sizeof(*i));
1587 i->completion = completion;
1588 i->context = context;
1589 i->servers = silc_dlist_init();
1595 /* Attach to resolving, if on going */
1596 server = silc_client_get_server_by_id(client, conn, server_id);
1597 if (server && server->internal.resolve_cmd_ident) {
1598 SILC_LOG_DEBUG(("Attach to existing resolving"));
1599 silc_client_unref_server(client, conn, server);
1600 silc_client_command_pending(conn, SILC_COMMAND_NONE,
1601 server->internal.resolve_cmd_ident,
1602 silc_client_get_server_cb, i);
1603 return server->internal.resolve_cmd_ident;
1606 /* Send the command */
1607 idp = silc_id_payload_encode(server_id, SILC_ID_SERVER);
1608 cmd_ident = silc_client_command_send(client, conn, SILC_COMMAND_IDENTIFY,
1609 silc_client_get_server_cb, i, 1,
1610 5, silc_buffer_datalen(idp));
1611 silc_buffer_free(idp);
1612 if (!cmd_ident && completion)
1613 completion(client, conn, SILC_STATUS_ERR_RESOURCE_LIMIT, NULL, context);
1615 if (server && cmd_ident)
1616 server->internal.resolve_cmd_ident = cmd_ident;
1618 silc_client_unref_server(client, conn, server);
1623 /************************** Server Entry Routines ***************************/
1625 /* Add new server entry */
1627 SilcServerEntry silc_client_add_server(SilcClient client,
1628 SilcClientConnection conn,
1629 const char *server_name,
1630 const char *server_info,
1631 SilcServerID *server_id)
1633 SilcServerEntry server_entry;
1634 char *server_namec = NULL;
1636 SILC_LOG_DEBUG(("Start"));
1638 server_entry = silc_calloc(1, sizeof(*server_entry));
1639 if (!server_entry || !server_id)
1642 server_entry->id = *server_id;
1644 server_entry->server_name = strdup(server_name);
1646 server_entry->server_info = strdup(server_info);
1648 /* Normalize server name */
1650 server_namec = silc_identifier_check(server_name, strlen(server_name),
1651 SILC_STRING_UTF8, 256, NULL);
1652 if (!server_namec) {
1653 silc_free(server_entry->server_name);
1654 silc_free(server_entry->server_info);
1655 silc_free(server_entry);
1660 silc_mutex_lock(conn->internal->lock);
1662 /* Add server to cache */
1663 if (!silc_idcache_add(conn->internal->server_cache, server_namec,
1664 &server_entry->id, server_entry)) {
1665 silc_free(server_namec);
1666 silc_free(server_entry->server_name);
1667 silc_free(server_entry->server_info);
1668 silc_free(server_entry);
1669 silc_mutex_unlock(conn->internal->lock);
1673 silc_mutex_unlock(conn->internal->lock);
1675 return server_entry;
1678 /* Removes server from the cache by the server entry. */
1680 SilcBool silc_client_del_server(SilcClient client, SilcClientConnection conn,
1681 SilcServerEntry server)
1683 SilcBool ret = silc_idcache_del_by_context(conn->internal->server_cache,
1685 silc_free(server->server_name);
1686 silc_free(server->server_info);
1691 /* Updates the `server_entry' with the new information sent as argument. */
1693 void silc_client_update_server(SilcClient client,
1694 SilcClientConnection conn,
1695 SilcServerEntry server_entry,
1696 const char *server_name,
1697 const char *server_info)
1699 char *server_namec = NULL;
1701 SILC_LOG_DEBUG(("Start"));
1704 (!server_entry->server_name ||
1705 !silc_utf8_strcasecmp(server_entry->server_name, server_name))) {
1707 silc_idcache_del_by_context(conn->internal->server_cache,
1708 server_entry, NULL);
1709 silc_free(server_entry->server_name);
1710 server_entry->server_name = strdup(server_name);
1712 /* Normalize server name */
1714 server_namec = silc_identifier_check(server_name, strlen(server_name),
1715 SILC_STRING_UTF8, 256, NULL);
1719 silc_idcache_add(conn->internal->server_cache, server_namec,
1720 &server_entry->id, server_entry);
1725 (!server_entry->server_info ||
1726 !silc_utf8_strcasecmp(server_entry->server_info, server_info))) {
1727 silc_free(server_entry->server_info);
1728 server_entry->server_info = strdup(server_info);
1732 /* Take reference of server entry */
1734 void silc_client_ref_server(SilcClient client, SilcClientConnection conn,
1735 SilcServerEntry server_entry)
1737 silc_atomic_add_int8(&server_entry->internal.refcnt, 1);
1740 /* Release reference of server entry */
1742 void silc_client_unref_server(SilcClient client, SilcClientConnection conn,
1743 SilcServerEntry server_entry)
1746 silc_atomic_sub_int8(&server_entry->internal.refcnt, 1) == 0)
1747 silc_client_del_server(client, conn, server_entry);
1750 /* Free server entry list */
1752 void silc_client_list_free_servers(SilcClient client,
1753 SilcClientConnection conn,
1754 SilcDList server_list)
1756 SilcServerEntry server_entry;
1759 silc_dlist_start(server_list);
1760 while ((server_entry = silc_dlist_get(server_list)))
1761 silc_client_unref_server(client, conn, server_entry);
1763 silc_dlist_uninit(server_list);