5 Author: Pekka Riikonen <priikone@silcnet.org>
7 Copyright (C) 2001 - 2003 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.
21 #include "silcincludes.h"
22 #include "silcclient.h"
23 #include "client_internal.h"
25 /******************************************************************************
27 Client Searching Locally
29 ******************************************************************************/
31 /* Same as silc_client_get_clients function but does not resolve anything
32 from the server. This checks local cache and returns all matching
33 clients from the local cache. If none was found this returns NULL.
34 The `nickname' is the real nickname of the client, and the `format'
35 is the formatted nickname to find exact match from multiple found
36 entries. The format must be same as given in the SilcClientParams
37 structure to the client library. If the `format' is NULL all found
38 clients by `nickname' are returned. */
40 SilcClientEntry *silc_client_get_clients_local(SilcClient client,
41 SilcClientConnection conn,
44 SilcUInt32 *clients_count)
46 SilcIDCacheEntry id_cache;
47 SilcIDCacheList list = NULL;
48 SilcClientEntry entry, *clients;
52 assert(client && conn);
56 /* Find ID from cache */
57 if (!silc_idcache_find_by_name(conn->internal->client_cache,
58 (char *)nickname, &list))
61 if (!silc_idcache_list_count(list)) {
62 silc_idcache_list_free(list);
66 clients = silc_calloc(silc_idcache_list_count(list), sizeof(*clients));
67 *clients_count = silc_idcache_list_count(list);
70 /* Take all without any further checking */
71 silc_idcache_list_first(list, &id_cache);
73 clients[i++] = id_cache->context;
75 if (!silc_idcache_list_next(list, &id_cache))
79 /* Check multiple cache entries for match */
80 silc_idcache_list_first(list, &id_cache);
82 entry = (SilcClientEntry)id_cache->context;
83 if (strcasecmp(entry->nickname, format)) {
84 if (!silc_idcache_list_next(list, &id_cache)) {
91 clients[i++] = id_cache->context;
93 if (!silc_idcache_list_next(list, &id_cache))
99 silc_idcache_list_free(list);
112 /******************************************************************************
114 Client Resolving from Server
116 ******************************************************************************/
120 SilcClientConnection conn;
121 SilcGetClientCallback completion;
125 } *GetClientInternal;
127 SILC_CLIENT_CMD_FUNC(get_client_callback)
129 GetClientInternal i = (GetClientInternal)context;
130 SilcClientEntry *clients;
131 SilcUInt32 clients_count;
133 /* Get the clients */
134 clients = silc_client_get_clients_local(i->client, i->conn,
135 i->nickname, i->server,
138 i->completion(i->client, i->conn, clients, clients_count, i->context);
141 i->completion(i->client, i->conn, NULL, 0, i->context);
144 silc_free(i->nickname);
145 silc_free(i->server);
149 /* Finds client entry or entries by the `nickname' and `server'. The
150 completion callback will be called when the client entries has been found.
152 Note: this function is always asynchronous and resolves the client
153 information from the server. Thus, if you already know the client
154 information then use the silc_client_get_client_by_id function to
155 get the client entry since this function may be very slow and should
156 be used only to initially get the client entries. */
158 void silc_client_get_clients(SilcClient client,
159 SilcClientConnection conn,
160 const char *nickname,
162 SilcGetClientCallback completion,
169 assert(client && conn);
174 i = silc_calloc(1, sizeof(*i));
177 i->nickname = strdup(nickname);
178 i->server = server ? strdup(server) : NULL;
179 i->completion = completion;
180 i->context = context;
182 if (nickname && server) {
183 len = strlen(nickname) + strlen(server) + 3;
184 userhost = silc_calloc(len, sizeof(*userhost));
185 silc_strncat(userhost, len, nickname, strlen(nickname));
186 silc_strncat(userhost, len, "@", 1);
187 silc_strncat(userhost, len, server, strlen(server));
189 userhost = silc_memdup(nickname, strlen(nickname));
192 /* Register our own command reply for this command */
193 silc_client_command_register(client, SILC_COMMAND_IDENTIFY, NULL, NULL,
194 silc_client_command_reply_identify_i, 0,
197 /* Send the command */
198 silc_client_command_send(client, conn, SILC_COMMAND_IDENTIFY,
199 conn->cmd_ident, 1, 1, userhost,
202 /* Add pending callback */
203 silc_client_command_pending(conn, SILC_COMMAND_IDENTIFY, conn->cmd_ident,
204 silc_client_command_get_client_callback,
210 /* The old style function to find client entry. This is used by the
211 library internally. If `query' is TRUE then the client information is
212 requested by the server. The pending command callback must be set
214 /* XXX This function should be removed */
216 SilcClientEntry silc_idlist_get_client(SilcClient client,
217 SilcClientConnection conn,
218 const char *nickname,
222 SilcIDCacheEntry id_cache;
223 SilcIDCacheList list = NULL;
224 SilcClientEntry entry = NULL;
226 SILC_LOG_DEBUG(("Start"));
228 /* Find ID from cache */
229 if (!silc_idcache_find_by_name(conn->internal->client_cache,
230 (char *)nickname, &list)) {
234 SILC_LOG_DEBUG(("Requesting Client ID from server"));
236 /* Register our own command reply for this command */
237 silc_client_command_register(client, SILC_COMMAND_IDENTIFY, NULL, NULL,
238 silc_client_command_reply_identify_i, 0,
241 /* Send the command */
242 silc_client_command_send(client, conn, SILC_COMMAND_IDENTIFY,
243 conn->cmd_ident, 1, 1, nickname,
247 silc_idcache_list_free(list);
255 /* Take first found cache entry */
256 if (!silc_idcache_list_first(list, &id_cache))
259 entry = (SilcClientEntry)id_cache->context;
261 /* Check multiple cache entries for match */
262 silc_idcache_list_first(list, &id_cache);
264 entry = (SilcClientEntry)id_cache->context;
266 if (strcasecmp(entry->nickname, format)) {
267 if (!silc_idcache_list_next(list, &id_cache)) {
279 /* If match weren't found, request it */
285 silc_idcache_list_free(list);
292 SilcClientConnection conn;
293 SilcUInt32 list_count;
294 SilcBuffer client_id_list;
295 SilcGetClientCallback completion;
298 } *GetClientsByListInternal;
300 SILC_CLIENT_CMD_FUNC(get_clients_list_callback)
302 GetClientsByListInternal i = (GetClientsByListInternal)context;
303 SilcIDCacheEntry id_cache = NULL;
304 SilcBuffer client_id_list = i->client_id_list;
305 SilcClientEntry *clients = NULL;
306 SilcUInt32 clients_count = 0;
310 SILC_LOG_DEBUG(("Start"));
318 SILC_LOG_DEBUG(("Resolved all clients"));
320 clients = silc_calloc(i->list_count, sizeof(*clients));
322 for (c = 0; c < i->list_count; c++) {
324 SilcClientID *client_id;
327 SILC_GET16_MSB(idp_len, client_id_list->data + 2);
329 client_id = silc_id_payload_parse_id(client_id_list->data, idp_len, NULL);
331 silc_buffer_pull(client_id_list, idp_len);
335 /* Get the client entry */
336 if (silc_idcache_find_by_id_one_ext(i->conn->internal->client_cache,
339 silc_hash_client_id_compare, NULL,
341 clients[clients_count] = (SilcClientEntry)id_cache->context;
346 silc_free(client_id);
347 silc_buffer_pull(client_id_list, idp_len);
351 i->completion(i->client, i->conn, clients, clients_count, i->context);
354 i->completion(i->client, i->conn, NULL, 0, i->context);
357 if (i->client_id_list)
358 silc_buffer_free(i->client_id_list);
362 /* Gets client entries by the list of client ID's `client_id_list'. This
363 always resolves those client ID's it does not know yet from the server
364 so this function might take a while. The `client_id_list' is a list
365 of ID Payloads added one after other. JOIN command reply and USERS
366 command reply for example returns this sort of list. The `completion'
367 will be called after the entries are available. */
369 void silc_client_get_clients_by_list(SilcClient client,
370 SilcClientConnection conn,
371 SilcUInt32 list_count,
372 SilcBuffer client_id_list,
373 SilcGetClientCallback completion,
376 SilcIDCacheEntry id_cache = NULL;
378 unsigned char **res_argv = NULL;
379 SilcUInt32 *res_argv_lens = NULL, *res_argv_types = NULL, res_argc = 0;
380 GetClientsByListInternal in;
381 bool wait_res = FALSE;
383 assert(client && conn && client_id_list);
385 SILC_LOG_DEBUG(("Start"));
387 in = silc_calloc(1, sizeof(*in));
390 in->list_count = list_count;
391 in->client_id_list = silc_buffer_copy(client_id_list);
392 in->completion = completion;
393 in->context = context;
395 for (i = 0; i < list_count; i++) {
397 SilcClientID *client_id;
398 SilcClientEntry entry;
402 SILC_GET16_MSB(idp_len, client_id_list->data + 2);
404 client_id = silc_id_payload_parse_id(client_id_list->data, idp_len, NULL);
406 silc_buffer_pull(client_id_list, idp_len);
410 /* Check if we have this client cached already. */
412 silc_idcache_find_by_id_one_ext(conn->internal->client_cache,
413 (void *)client_id, NULL, NULL,
414 silc_hash_client_id_compare, NULL,
417 /* If we don't have the entry or it has incomplete info, then resolve
418 it from the server. */
419 if (!ret || !((SilcClientEntry)id_cache->context)->nickname) {
420 entry = ret ? (SilcClientEntry)id_cache->context : NULL;
423 if (entry->status & SILC_CLIENT_STATUS_RESOLVING) {
424 /* Attach to this resolving and wait until it finishes */
425 silc_client_command_pending(
426 conn, SILC_COMMAND_NONE,
427 entry->resolve_cmd_ident,
428 silc_client_command_get_clients_list_callback,
433 silc_free(client_id);
434 silc_buffer_pull(client_id_list, idp_len);
438 entry->status |= SILC_CLIENT_STATUS_RESOLVING;
439 entry->resolve_cmd_ident = conn->cmd_ident + 1;
442 /* No we don't have it, query it from the server. Assemble argument
443 table that will be sent for the IDENTIFY command later. */
444 res_argv = silc_realloc(res_argv, sizeof(*res_argv) *
446 res_argv_lens = silc_realloc(res_argv_lens, sizeof(*res_argv_lens) *
448 res_argv_types = silc_realloc(res_argv_types, sizeof(*res_argv_types) *
450 res_argv[res_argc] = client_id_list->data;
451 res_argv_lens[res_argc] = idp_len;
452 res_argv_types[res_argc] = res_argc + 5;
456 silc_free(client_id);
457 silc_buffer_pull(client_id_list, idp_len);
460 silc_buffer_push(client_id_list, client_id_list->data -
461 client_id_list->head);
463 /* Query the client information from server if the list included clients
464 that we don't know about. */
468 /* Send the IDENTIFY command to server */
469 res_cmd = silc_command_payload_encode(SILC_COMMAND_IDENTIFY,
470 res_argc, res_argv, res_argv_lens,
471 res_argv_types, ++conn->cmd_ident);
472 silc_client_packet_send(client, conn->sock, SILC_PACKET_COMMAND,
473 NULL, 0, NULL, NULL, res_cmd->data, res_cmd->len,
476 /* Register our own command reply for this command */
477 silc_client_command_register(client, SILC_COMMAND_IDENTIFY, NULL, NULL,
478 silc_client_command_reply_identify_i, 0,
481 /* Process the applications request after reply has been received */
482 silc_client_command_pending(conn, SILC_COMMAND_IDENTIFY, conn->cmd_ident,
483 silc_client_command_get_clients_list_callback,
487 silc_buffer_free(res_cmd);
489 silc_free(res_argv_lens);
490 silc_free(res_argv_types);
497 /* We have the clients in cache, get them and call the completion */
498 silc_client_command_get_clients_list_callback((void *)in, NULL);
503 SilcClientConnection conn;
504 SilcChannelID channel_id;
505 SilcGetClientCallback completion;
508 } *GetClientsByChannelInternal;
510 SILC_CLIENT_CMD_FUNC(get_clients_by_channel_cb)
512 GetClientsByChannelInternal i = context;
513 SilcClientEntry *clients = NULL;
514 SilcUInt32 clients_count = 0;
516 SilcChannelEntry channel;
517 SilcHashTableList htl;
520 channel = silc_client_get_channel_by_id(i->client, i->conn, &i->channel_id);
521 if (channel && !silc_hash_table_count(channel->user_list)) {
522 clients = silc_calloc(silc_hash_table_count(channel->user_list),
524 silc_hash_table_list(channel->user_list, &htl);
525 while (silc_hash_table_get(&htl, NULL, (void **)&chu))
526 clients[clients_count++] = chu->client;
527 silc_hash_table_list_reset(&htl);
532 i->completion(i->client, i->conn, clients, clients_count, i->context);
535 i->completion(i->client, i->conn, NULL, 0, i->context);
541 /* Gets client entries by the channel entry indicated by `channel'. Thus,
542 it resolves the clients currently on that channel. */
544 void silc_client_get_clients_by_channel(SilcClient client,
545 SilcClientConnection conn,
546 SilcChannelEntry channel,
547 SilcGetClientCallback completion,
550 GetClientsByChannelInternal in;
551 SilcHashTableList htl;
553 SilcClientEntry entry;
554 unsigned char **res_argv = NULL;
555 SilcUInt32 *res_argv_lens = NULL, *res_argv_types = NULL, res_argc = 0;
557 bool wait_res = FALSE;
559 assert(client && conn && channel);
561 SILC_LOG_DEBUG(("Start"));
563 in = silc_calloc(1, sizeof(*in));
566 in->channel_id = *channel->id;
567 in->completion = completion;
568 in->context = context;
570 /* If user list does not exist, send USERS command. */
571 if (!channel->user_list || !silc_hash_table_count(channel->user_list)) {
572 SILC_LOG_DEBUG(("Sending USERS"));
573 silc_client_command_register(client, SILC_COMMAND_USERS, NULL, NULL,
574 silc_client_command_reply_users_i, 0,
576 silc_client_command_send(client, conn, SILC_COMMAND_USERS,
577 conn->cmd_ident, 1, 2, channel->channel_name,
578 strlen(channel->channel_name));
579 silc_client_command_pending(conn, SILC_COMMAND_USERS, conn->cmd_ident,
580 silc_client_command_get_clients_by_channel_cb,
585 silc_hash_table_list(channel->user_list, &htl);
586 while (silc_hash_table_get(&htl, NULL, (void **)&chu)) {
589 /* If the entry has incomplete info, then resolve it from the server. */
590 if (!entry->nickname || !entry->realname) {
591 if (entry->status & SILC_CLIENT_STATUS_RESOLVING) {
592 /* Attach to this resolving and wait until it finishes */
593 silc_client_command_pending(
594 conn, SILC_COMMAND_NONE,
595 entry->resolve_cmd_ident,
596 silc_client_command_get_clients_by_channel_cb,
602 entry->status |= SILC_CLIENT_STATUS_RESOLVING;
603 entry->resolve_cmd_ident = conn->cmd_ident + 1;
605 idp = silc_id_payload_encode(entry->id, SILC_ID_CLIENT);
607 /* No we don't have it, query it from the server. Assemble argument
608 table that will be sent for the WHOIS command later. */
609 res_argv = silc_realloc(res_argv, sizeof(*res_argv) *
611 res_argv_lens = silc_realloc(res_argv_lens, sizeof(*res_argv_lens) *
613 res_argv_types = silc_realloc(res_argv_types, sizeof(*res_argv_types) *
615 res_argv[res_argc] = silc_memdup(idp->data, idp->len);
616 res_argv_lens[res_argc] = idp->len;
617 res_argv_types[res_argc] = res_argc + 4;
620 silc_buffer_free(idp);
623 silc_hash_table_list_reset(&htl);
625 /* Query the client information from server if the list included clients
626 that we don't know about. */
630 /* Send the WHOIS command to server */
631 res_cmd = silc_command_payload_encode(SILC_COMMAND_WHOIS,
632 res_argc, res_argv, res_argv_lens,
633 res_argv_types, ++conn->cmd_ident);
634 silc_client_packet_send(client, conn->sock, SILC_PACKET_COMMAND,
635 NULL, 0, NULL, NULL, res_cmd->data, res_cmd->len,
638 /* Register our own command reply for this command */
639 silc_client_command_register(client, SILC_COMMAND_WHOIS, NULL, NULL,
640 silc_client_command_reply_whois_i, 0,
643 /* Process the applications request after reply has been received */
644 silc_client_command_pending(
645 conn, SILC_COMMAND_WHOIS, conn->cmd_ident,
646 silc_client_command_get_clients_by_channel_cb,
650 silc_buffer_free(res_cmd);
652 silc_free(res_argv_lens);
653 silc_free(res_argv_types);
660 /* We have the clients in cache, get them and call the completion */
661 silc_client_command_get_clients_by_channel_cb((void *)in, NULL);
664 /* Finds entry for client by the client's ID. Returns the entry or NULL
665 if the entry was not found. */
667 SilcClientEntry silc_client_get_client_by_id(SilcClient client,
668 SilcClientConnection conn,
669 SilcClientID *client_id)
671 SilcIDCacheEntry id_cache;
673 assert(client && conn);
677 SILC_LOG_DEBUG(("Finding client by ID (%s)",
678 silc_id_render(client_id, SILC_ID_CLIENT)));
680 /* Find ID from cache */
681 if (!silc_idcache_find_by_id_one_ext(conn->internal->client_cache,
682 (void *)client_id, NULL, NULL,
683 silc_hash_client_id_compare, NULL,
687 SILC_LOG_DEBUG(("Found"));
689 return (SilcClientEntry)id_cache->context;
694 SilcClientConnection conn;
695 SilcClientID *client_id;
696 SilcGetClientCallback completion;
698 } *GetClientByIDInternal;
700 SILC_CLIENT_CMD_FUNC(get_client_by_id_callback)
702 GetClientByIDInternal i = (GetClientByIDInternal)context;
703 SilcClientEntry entry;
706 entry = silc_client_get_client_by_id(i->client, i->conn, i->client_id);
709 i->completion(i->client, i->conn, &entry, 1, i->context);
712 i->completion(i->client, i->conn, NULL, 0, i->context);
715 silc_free(i->client_id);
719 /* Same as above but will always resolve the information from the server.
720 Use this only if you know that you don't have the entry and the only
721 thing you know about the client is its ID. */
723 void silc_client_get_client_by_id_resolve(SilcClient client,
724 SilcClientConnection conn,
725 SilcClientID *client_id,
726 SilcBuffer attributes,
727 SilcGetClientCallback completion,
731 GetClientByIDInternal i = silc_calloc(1, sizeof(*i));
733 assert(client && conn && client_id);
735 SILC_LOG_DEBUG(("Start"));
739 i->client_id = silc_id_dup(client_id, SILC_ID_CLIENT);
740 i->completion = completion;
741 i->context = context;
743 /* Register our own command reply for this command */
744 silc_client_command_register(client, SILC_COMMAND_WHOIS, NULL, NULL,
745 silc_client_command_reply_whois_i, 0,
748 /* Send the command */
749 idp = silc_id_payload_encode(client_id, SILC_ID_CLIENT);
750 silc_client_command_send(client, conn, SILC_COMMAND_WHOIS, conn->cmd_ident,
751 2, 3, attributes ? attributes->data : NULL,
752 attributes ? attributes->len : 0,
753 4, idp->data, idp->len);
754 silc_buffer_free(idp);
756 /* Add pending callback */
757 silc_client_command_pending(conn, SILC_COMMAND_WHOIS, conn->cmd_ident,
758 silc_client_command_get_client_by_id_callback,
763 /******************************************************************************
765 Client, Channel and Server entry manipulation
767 ******************************************************************************/
770 /* Creates new client entry and adds it to the ID cache. Returns pointer
774 silc_client_add_client(SilcClient client, SilcClientConnection conn,
775 char *nickname, char *username,
776 char *userinfo, SilcClientID *id, SilcUInt32 mode)
778 SilcClientEntry client_entry;
781 SILC_LOG_DEBUG(("Start"));
783 /* Save the client infos */
784 client_entry = silc_calloc(1, sizeof(*client_entry));
785 client_entry->id = id;
786 client_entry->valid = TRUE;
787 silc_parse_userfqdn(nickname, &nick, &client_entry->server);
788 silc_parse_userfqdn(username, &client_entry->username,
789 &client_entry->hostname);
791 client_entry->realname = strdup(userinfo);
792 client_entry->mode = mode;
794 client_entry->nickname = strdup(nick);
795 client_entry->channels = silc_hash_table_alloc(1, silc_hash_ptr, NULL, NULL,
796 NULL, NULL, NULL, TRUE);
798 /* Format the nickname */
799 silc_client_nickname_format(client, conn, client_entry);
801 /* Add client to cache, the non-formatted nickname is saved to cache */
802 if (!silc_idcache_add(conn->internal->client_cache, nick, client_entry->id,
803 (void *)client_entry, 0, NULL)) {
804 silc_free(client_entry->nickname);
805 silc_free(client_entry->username);
806 silc_free(client_entry->hostname);
807 silc_free(client_entry->server);
808 silc_hash_table_free(client_entry->channels);
809 silc_free(client_entry);
816 /* Updates the `client_entry' with the new information sent as argument. */
818 void silc_client_update_client(SilcClient client,
819 SilcClientConnection conn,
820 SilcClientEntry client_entry,
821 const char *nickname,
822 const char *username,
823 const char *userinfo,
828 SILC_LOG_DEBUG(("Start"));
830 if ((!client_entry->username || !client_entry->hostname) && username) {
831 silc_free(client_entry->username);
832 silc_free(client_entry->hostname);
833 client_entry->username = NULL;
834 client_entry->hostname = NULL;
835 silc_parse_userfqdn(username, &client_entry->username,
836 &client_entry->hostname);
838 if (!client_entry->realname && userinfo)
839 client_entry->realname = strdup(userinfo);
840 if (!client_entry->nickname && nickname) {
841 silc_parse_userfqdn(nickname, &nick, &client_entry->server);
842 client_entry->nickname = strdup(nick);
843 silc_client_nickname_format(client, conn, client_entry);
845 client_entry->mode = mode;
848 /* Remove the old cache entry and create a new one */
849 silc_idcache_del_by_context(conn->internal->client_cache, client_entry);
850 silc_idcache_add(conn->internal->client_cache, nick, client_entry->id,
851 client_entry, 0, NULL);
855 /* Deletes the client entry and frees all memory. */
857 void silc_client_del_client_entry(SilcClient client,
858 SilcClientConnection conn,
859 SilcClientEntry client_entry)
861 SILC_LOG_DEBUG(("Start"));
863 silc_free(client_entry->nickname);
864 silc_free(client_entry->username);
865 silc_free(client_entry->realname);
866 silc_free(client_entry->hostname);
867 silc_free(client_entry->server);
868 silc_free(client_entry->id);
869 silc_free(client_entry->fingerprint);
870 silc_hash_table_free(client_entry->channels);
871 if (client_entry->send_key)
872 silc_cipher_free(client_entry->send_key);
873 if (client_entry->receive_key)
874 silc_cipher_free(client_entry->receive_key);
875 silc_free(client_entry->key);
876 silc_client_ftp_session_free_client(conn, client_entry);
877 if (client_entry->ke)
878 silc_client_abort_key_agreement(client, conn, client_entry);
879 silc_free(client_entry);
882 /* Removes client from the cache by the client entry. */
884 bool silc_client_del_client(SilcClient client, SilcClientConnection conn,
885 SilcClientEntry client_entry)
887 bool ret = silc_idcache_del_by_context(conn->internal->client_cache,
891 /* Remove from channels */
892 silc_client_remove_from_channels(client, conn, client_entry);
894 /* Free the client entry data */
895 silc_client_del_client_entry(client, conn, client_entry);
901 /* Add new channel entry to the ID Cache */
903 SilcChannelEntry silc_client_add_channel(SilcClient client,
904 SilcClientConnection conn,
905 const char *channel_name,
907 SilcChannelID *channel_id)
909 SilcChannelEntry channel;
911 SILC_LOG_DEBUG(("Start"));
913 channel = silc_calloc(1, sizeof(*channel));
914 channel->channel_name = strdup(channel_name);
915 channel->id = channel_id;
916 channel->mode = mode;
917 channel->user_list = silc_hash_table_alloc(1, silc_hash_ptr, NULL, NULL,
918 NULL, NULL, NULL, TRUE);
920 /* Put it to the ID cache */
921 if (!silc_idcache_add(conn->internal->channel_cache, channel->channel_name,
922 (void *)channel->id, (void *)channel, 0, NULL)) {
923 silc_free(channel->channel_name);
924 silc_hash_table_free(channel->user_list);
932 /* Foreach callbcak to free all users from the channel when deleting a
935 static void silc_client_del_channel_foreach(void *key, void *context,
938 SilcChannelUser chu = (SilcChannelUser)context;
940 SILC_LOG_DEBUG(("Start"));
942 /* Remove the context from the client's channel hash table as that
943 table and channel's user_list hash table share this same context. */
944 silc_hash_table_del(chu->client->channels, chu->channel);
948 /* Removes channel from the cache by the channel entry. */
950 bool silc_client_del_channel(SilcClient client, SilcClientConnection conn,
951 SilcChannelEntry channel)
953 bool ret = silc_idcache_del_by_context(conn->internal->channel_cache,
956 SILC_LOG_DEBUG(("Start"));
958 /* Free all client entrys from the users list. The silc_hash_table_free
959 will free all the entries so they are not freed at the foreach
961 silc_hash_table_foreach(channel->user_list, silc_client_del_channel_foreach,
963 silc_hash_table_free(channel->user_list);
965 silc_free(channel->channel_name);
966 silc_free(channel->id);
967 silc_free(channel->key);
968 if (channel->channel_key)
969 silc_cipher_free(channel->channel_key);
971 silc_hmac_free(channel->hmac);
972 if (channel->old_channel_keys) {
974 silc_dlist_start(channel->old_channel_keys);
975 while ((key = silc_dlist_get(channel->old_channel_keys)) != SILC_LIST_END)
976 silc_cipher_free(key);
977 silc_dlist_uninit(channel->old_channel_keys);
979 if (channel->old_hmacs) {
981 silc_dlist_start(channel->old_hmacs);
982 while ((hmac = silc_dlist_get(channel->old_hmacs)) != SILC_LIST_END)
983 silc_hmac_free(hmac);
984 silc_dlist_uninit(channel->old_hmacs);
986 silc_client_del_channel_private_keys(client, conn, channel);
991 /* Replaces the channel ID of the `channel' to `new_id'. Returns FALSE
992 if the ID could not be changed. */
994 bool silc_client_replace_channel_id(SilcClient client,
995 SilcClientConnection conn,
996 SilcChannelEntry channel,
997 SilcChannelID *new_id)
1002 SILC_LOG_DEBUG(("Old Channel ID id(%s)",
1003 silc_id_render(channel->id, SILC_ID_CHANNEL)));
1004 SILC_LOG_DEBUG(("New Channel ID id(%s)",
1005 silc_id_render(new_id, SILC_ID_CHANNEL)));
1007 silc_idcache_del_by_id(conn->internal->channel_cache, channel->id);
1008 silc_free(channel->id);
1009 channel->id = new_id;
1010 return silc_idcache_add(conn->internal->channel_cache,
1011 channel->channel_name,
1012 (void *)channel->id, (void *)channel, 0, NULL);
1016 /* Finds entry for channel by the channel name. Returns the entry or NULL
1017 if the entry was not found. It is found only if the client is joined
1020 SilcChannelEntry silc_client_get_channel(SilcClient client,
1021 SilcClientConnection conn,
1024 SilcIDCacheEntry id_cache;
1025 SilcChannelEntry entry;
1027 assert(client && conn);
1031 SILC_LOG_DEBUG(("Start"));
1033 if (!silc_idcache_find_by_name_one(conn->internal->channel_cache, channel,
1037 entry = (SilcChannelEntry)id_cache->context;
1039 SILC_LOG_DEBUG(("Found"));
1044 /* Finds entry for channel by the channel ID. Returns the entry or NULL
1045 if the entry was not found. It is found only if the client is joined
1048 SilcChannelEntry silc_client_get_channel_by_id(SilcClient client,
1049 SilcClientConnection conn,
1050 SilcChannelID *channel_id)
1052 SilcIDCacheEntry id_cache;
1053 SilcChannelEntry entry;
1055 assert(client && conn);
1059 SILC_LOG_DEBUG(("Start"));
1061 if (!silc_idcache_find_by_id_one(conn->internal->channel_cache, channel_id,
1065 entry = (SilcChannelEntry)id_cache->context;
1067 SILC_LOG_DEBUG(("Found"));
1074 SilcClientConnection conn;
1076 SilcChannelID *channel_id;
1079 SilcGetChannelCallback completion;
1081 } *GetChannelInternal;
1083 SILC_CLIENT_CMD_FUNC(get_channel_resolve_callback)
1085 GetChannelInternal i = (GetChannelInternal)context;
1086 SilcChannelEntry entry;
1088 SILC_LOG_DEBUG(("Start"));
1090 /* Get the channel */
1091 entry = silc_client_get_channel(i->client, i->conn, i->u.channel_name);
1093 i->completion(i->client, i->conn, &entry, 1, i->context);
1095 i->completion(i->client, i->conn, NULL, 0, i->context);
1098 silc_free(i->u.channel_name);
1102 /* Resolves channel entry from the server by the channel name. */
1104 void silc_client_get_channel_resolve(SilcClient client,
1105 SilcClientConnection conn,
1107 SilcGetChannelCallback completion,
1110 GetChannelInternal i = silc_calloc(1, sizeof(*i));
1112 assert(client && conn && channel_name);
1114 SILC_LOG_DEBUG(("Start"));
1118 i->u.channel_name = strdup(channel_name);
1119 i->completion = completion;
1120 i->context = context;
1122 /* Register our own command reply for this command */
1123 silc_client_command_register(client, SILC_COMMAND_IDENTIFY, NULL, NULL,
1124 silc_client_command_reply_identify_i, 0,
1127 /* Send the command */
1128 silc_client_command_send(client, conn, SILC_COMMAND_IDENTIFY,
1130 1, 3, channel_name, strlen(channel_name));
1132 /* Add pending callback */
1133 silc_client_command_pending(conn, SILC_COMMAND_IDENTIFY, conn->cmd_ident,
1134 silc_client_command_get_channel_resolve_callback,
1138 SILC_CLIENT_CMD_FUNC(get_channel_by_id_callback)
1140 GetChannelInternal i = (GetChannelInternal)context;
1141 SilcChannelEntry entry;
1143 SILC_LOG_DEBUG(("Start"));
1145 /* Get the channel */
1146 entry = silc_client_get_channel_by_id(i->client, i->conn, i->u.channel_id);
1148 i->completion(i->client, i->conn, &entry, 1, i->context);
1150 i->completion(i->client, i->conn, NULL, 0, i->context);
1153 silc_free(i->u.channel_id);
1157 /* Resolves channel information from the server by the channel ID. */
1159 void silc_client_get_channel_by_id_resolve(SilcClient client,
1160 SilcClientConnection conn,
1161 SilcChannelID *channel_id,
1162 SilcGetChannelCallback completion,
1166 GetChannelInternal i = silc_calloc(1, sizeof(*i));
1168 assert(client && conn && channel_id);
1170 SILC_LOG_DEBUG(("Start"));
1174 i->u.channel_id = silc_id_dup(channel_id, SILC_ID_CHANNEL);
1175 i->completion = completion;
1176 i->context = context;
1178 /* Register our own command reply for this command */
1179 silc_client_command_register(client, SILC_COMMAND_IDENTIFY, NULL, NULL,
1180 silc_client_command_reply_identify_i, 0,
1183 /* Send the command */
1184 idp = silc_id_payload_encode(channel_id, SILC_ID_CHANNEL);
1185 silc_client_command_send(client, conn, SILC_COMMAND_IDENTIFY,
1187 1, 5, idp->data, idp->len);
1188 silc_buffer_free(idp);
1190 /* Add pending callback */
1191 silc_client_command_pending(conn, SILC_COMMAND_IDENTIFY, conn->cmd_ident,
1192 silc_client_command_get_channel_by_id_callback,
1196 /* Finds entry for server by the server name. */
1198 SilcServerEntry silc_client_get_server(SilcClient client,
1199 SilcClientConnection conn,
1202 SilcIDCacheEntry id_cache;
1203 SilcServerEntry entry;
1205 assert(client && conn);
1209 SILC_LOG_DEBUG(("Start"));
1211 if (!silc_idcache_find_by_name_one(conn->internal->server_cache,
1212 server_name, &id_cache))
1215 entry = (SilcServerEntry)id_cache->context;
1220 /* Finds entry for server by the server ID. */
1222 SilcServerEntry silc_client_get_server_by_id(SilcClient client,
1223 SilcClientConnection conn,
1224 SilcServerID *server_id)
1226 SilcIDCacheEntry id_cache;
1227 SilcServerEntry entry;
1229 assert(client && conn);
1233 SILC_LOG_DEBUG(("Start"));
1235 if (!silc_idcache_find_by_id_one(conn->internal->server_cache,
1236 (void *)server_id, &id_cache))
1239 entry = (SilcServerEntry)id_cache->context;
1244 /* Add new server entry */
1246 SilcServerEntry silc_client_add_server(SilcClient client,
1247 SilcClientConnection conn,
1248 const char *server_name,
1249 const char *server_info,
1250 SilcServerID *server_id)
1252 SilcServerEntry server_entry;
1254 SILC_LOG_DEBUG(("Start"));
1256 server_entry = silc_calloc(1, sizeof(*server_entry));
1257 if (!server_entry || !server_id)
1260 server_entry->server_id = server_id;
1262 server_entry->server_name = strdup(server_name);
1264 server_entry->server_info = strdup(server_info);
1266 /* Add server to cache */
1267 if (!silc_idcache_add(conn->internal->server_cache,
1268 server_entry->server_name,
1269 server_entry->server_id, server_entry, 0, NULL)) {
1270 silc_free(server_entry->server_id);
1271 silc_free(server_entry->server_name);
1272 silc_free(server_entry->server_info);
1273 silc_free(server_entry);
1277 return server_entry;
1280 /* Removes server from the cache by the server entry. */
1282 bool silc_client_del_server(SilcClient client, SilcClientConnection conn,
1283 SilcServerEntry server)
1285 bool ret = silc_idcache_del_by_context(conn->internal->server_cache, server);
1286 silc_free(server->server_name);
1287 silc_free(server->server_info);
1288 silc_free(server->server_id);
1293 /* Updates the `server_entry' with the new information sent as argument. */
1295 void silc_client_update_server(SilcClient client,
1296 SilcClientConnection conn,
1297 SilcServerEntry server_entry,
1298 const char *server_name,
1299 const char *server_info)
1301 SILC_LOG_DEBUG(("Start"));
1303 if (server_name && (!server_entry->server_name ||
1304 strcmp(server_entry->server_name, server_name))) {
1306 silc_idcache_del_by_context(conn->internal->server_cache, server_entry);
1307 silc_free(server_entry->server_name);
1308 server_entry->server_name = strdup(server_name);
1309 silc_idcache_add(conn->internal->server_cache, server_entry->server_name,
1310 server_entry->server_id,
1311 server_entry, 0, NULL);
1314 if (server_info && (!server_entry->server_info ||
1315 strcmp(server_entry->server_info, server_info))) {
1316 silc_free(server_entry->server_info);
1317 server_entry->server_info = strdup(server_info);
1321 /* Formats the nickname of the client specified by the `client_entry'.
1322 If the format is specified by the application this will format the
1323 nickname and replace the old nickname in the client entry. If the
1324 format string is not specified then this function has no effect. */
1326 void silc_client_nickname_format(SilcClient client,
1327 SilcClientConnection conn,
1328 SilcClientEntry client_entry)
1331 char *newnick = NULL;
1332 int i, off = 0, len;
1334 SilcClientEntry *clients;
1335 SilcUInt32 clients_count = 0;
1336 SilcClientEntry unformatted = NULL;
1338 SILC_LOG_DEBUG(("Start"));
1340 if (!client->internal->params->nickname_format[0])
1343 if (!client_entry->nickname)
1346 /* Get all clients with same nickname. Do not perform the formatting
1347 if there aren't any clients with same nickname unless the application
1348 is forcing us to do so. */
1349 clients = silc_client_get_clients_local(client, conn,
1350 client_entry->nickname, NULL,
1352 if (!clients && !client->internal->params->nickname_force_format)
1357 for (i = 0; i < clients_count; i++) {
1358 if (clients[i]->valid && clients[i] != client_entry)
1360 if (clients[i]->valid && clients[i] != client_entry &&
1361 !strcmp(clients[i]->nickname, client_entry->nickname))
1364 if (!len || freebase)
1367 if (clients_count == 1)
1368 unformatted = clients[0];
1370 for (i = 0; i < clients_count; i++)
1371 if (!strncasecmp(clients[i]->nickname, client_entry->nickname,
1372 strlen(clients[i]->nickname)))
1373 unformatted = clients[i];
1375 /* If we are changing nickname of our local entry we'll enforce
1376 that we will always get the unformatted nickname. Give our
1377 format number to the one that is not formatted now. */
1378 if (unformatted && client_entry == conn->local_entry)
1379 client_entry = unformatted;
1381 cp = client->internal->params->nickname_format;
1391 if (!client_entry->nickname)
1393 len = strlen(client_entry->nickname);
1394 newnick = silc_realloc(newnick, sizeof(*newnick) * (off + len));
1395 memcpy(&newnick[off], client_entry->nickname, len);
1399 /* Stripped hostname */
1400 if (!client_entry->hostname)
1402 len = strcspn(client_entry->hostname, ".");
1403 i = strcspn(client_entry->hostname, "-");
1406 newnick = silc_realloc(newnick, sizeof(*newnick) * (off + len));
1407 memcpy(&newnick[off], client_entry->hostname, len);
1412 if (!client_entry->hostname)
1414 len = strlen(client_entry->hostname);
1415 newnick = silc_realloc(newnick, sizeof(*newnick) * (off + len));
1416 memcpy(&newnick[off], client_entry->hostname, len);
1420 /* Stripped server name */
1421 if (!client_entry->server)
1423 len = strcspn(client_entry->server, ".");
1424 newnick = silc_realloc(newnick, sizeof(*newnick) * (off + len));
1425 memcpy(&newnick[off], client_entry->server, len);
1429 /* Full server name */
1430 if (!client_entry->server)
1432 len = strlen(client_entry->server);
1433 newnick = silc_realloc(newnick, sizeof(*newnick) * (off + len));
1434 memcpy(&newnick[off], client_entry->server, len);
1438 /* Ascending number */
1443 if (clients_count == 1)
1446 for (i = 0; i < clients_count; i++) {
1447 if (strncasecmp(clients[i]->nickname, newnick, off))
1449 if (strlen(clients[i]->nickname) <= off)
1451 num = atoi(&clients[i]->nickname[off]);
1456 memset(tmp, 0, sizeof(tmp));
1457 snprintf(tmp, sizeof(tmp) - 1, "%d", ++max);
1459 newnick = silc_realloc(newnick, sizeof(*newnick) * (off + len));
1460 memcpy(&newnick[off], tmp, len);
1465 /* Some other character in the string */
1466 newnick = silc_realloc(newnick, sizeof(*newnick) * (off + 1));
1467 memcpy(&newnick[off], cp, 1);
1475 newnick = silc_realloc(newnick, sizeof(*newnick) * (off + 1));
1478 silc_free(client_entry->nickname);
1479 client_entry->nickname = newnick;