5 Author: Pekka Riikonen <priikone@silcnet.org>
7 Copyright (C) 2001 - 2004 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,
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_i(SilcClient client,
159 SilcClientConnection conn,
161 const char *nickname,
163 SilcBuffer attributes,
164 SilcGetClientCallback completion,
171 assert(client && conn);
176 i = silc_calloc(1, sizeof(*i));
179 i->nickname = strdup(nickname);
180 i->server = server ? strdup(server) : NULL;
181 i->completion = completion;
182 i->context = context;
184 if (nickname && server) {
185 len = strlen(nickname) + strlen(server) + 3;
186 userhost = silc_calloc(len, sizeof(*userhost));
187 silc_strncat(userhost, len, nickname, strlen(nickname));
188 silc_strncat(userhost, len, "@", 1);
189 silc_strncat(userhost, len, server, strlen(server));
191 userhost = silc_memdup(nickname, strlen(nickname));
194 /* Register our own command reply for this command */
195 if (command == SILC_COMMAND_IDENTIFY) {
196 silc_client_command_register(client, command, NULL, NULL,
197 silc_client_command_reply_identify_i, 0,
199 /* Send the command */
200 silc_client_command_send(client, conn, SILC_COMMAND_IDENTIFY,
201 conn->cmd_ident, 1, 1, userhost,
204 silc_client_command_register(client, command, NULL, NULL,
205 silc_client_command_reply_whois_i, 0,
207 /* Send the command */
208 silc_client_command_send(client, conn, command, conn->cmd_ident, 2,
209 1, userhost, strlen(userhost),
210 3, attributes ? attributes->data : NULL,
211 attributes ? attributes->len : 0);
214 /* Add pending callback */
215 silc_client_command_pending(conn, command, conn->cmd_ident,
216 silc_client_command_get_client_callback,
221 void silc_client_get_clients(SilcClient client,
222 SilcClientConnection conn,
223 const char *nickname,
225 SilcGetClientCallback completion,
228 silc_client_get_clients_i(client, conn, SILC_COMMAND_IDENTIFY,
229 nickname, server, NULL,
230 completion, context);
233 void silc_client_get_clients_whois(SilcClient client,
234 SilcClientConnection conn,
235 const char *nickname,
237 SilcBuffer attributes,
238 SilcGetClientCallback completion,
241 silc_client_get_clients_i(client, conn, SILC_COMMAND_WHOIS,
242 nickname, server, attributes,
243 completion, context);
246 /* The old style function to find client entry. This is used by the
247 library internally. If `query' is TRUE then the client information is
248 requested by the server. The pending command callback must be set
250 /* XXX This function should be removed */
252 SilcClientEntry silc_idlist_get_client(SilcClient client,
253 SilcClientConnection conn,
254 const char *nickname,
258 SilcIDCacheEntry id_cache;
259 SilcIDCacheList list = NULL;
260 SilcClientEntry entry = NULL;
262 SILC_LOG_DEBUG(("Start"));
264 /* Find ID from cache */
265 if (!silc_idcache_find_by_name(conn->internal->client_cache,
266 (char *)nickname, &list)) {
270 SILC_LOG_DEBUG(("Requesting Client ID from server"));
272 /* Register our own command reply for this command */
273 silc_client_command_register(client, SILC_COMMAND_IDENTIFY, NULL, NULL,
274 silc_client_command_reply_identify_i, 0,
277 /* Send the command */
278 silc_client_command_send(client, conn, SILC_COMMAND_IDENTIFY,
279 conn->cmd_ident, 1, 1, nickname,
283 silc_idcache_list_free(list);
291 /* Take first found cache entry */
292 if (!silc_idcache_list_first(list, &id_cache))
295 entry = (SilcClientEntry)id_cache->context;
297 /* Check multiple cache entries for match */
298 silc_idcache_list_first(list, &id_cache);
300 entry = (SilcClientEntry)id_cache->context;
302 if (strcasecmp(entry->nickname, format)) {
303 if (!silc_idcache_list_next(list, &id_cache)) {
315 /* If match weren't found, request it */
321 silc_idcache_list_free(list);
328 SilcClientConnection conn;
329 SilcUInt32 list_count;
330 SilcBuffer client_id_list;
331 SilcGetClientCallback completion;
334 } *GetClientsByListInternal;
336 SILC_CLIENT_CMD_FUNC(get_clients_list_callback)
338 GetClientsByListInternal i = (GetClientsByListInternal)context;
339 SilcIDCacheEntry id_cache = NULL;
340 SilcBuffer client_id_list = i->client_id_list;
341 SilcClientEntry *clients = NULL;
342 SilcUInt32 clients_count = 0;
346 SILC_LOG_DEBUG(("Start"));
354 SILC_LOG_DEBUG(("Resolved all clients"));
356 clients = silc_calloc(i->list_count, sizeof(*clients));
358 for (c = 0; c < i->list_count; c++) {
360 SilcClientID *client_id;
363 SILC_GET16_MSB(idp_len, client_id_list->data + 2);
365 client_id = silc_id_payload_parse_id(client_id_list->data, idp_len, NULL);
367 silc_buffer_pull(client_id_list, idp_len);
371 /* Get the client entry */
372 if (silc_idcache_find_by_id_one_ext(i->conn->internal->client_cache,
375 silc_hash_client_id_compare, NULL,
377 clients[clients_count] = (SilcClientEntry)id_cache->context;
382 silc_free(client_id);
383 silc_buffer_pull(client_id_list, idp_len);
387 i->completion(i->client, i->conn, clients, clients_count, i->context);
390 i->completion(i->client, i->conn, NULL, 0, i->context);
393 if (i->client_id_list)
394 silc_buffer_free(i->client_id_list);
398 /* Gets client entries by the list of client ID's `client_id_list'. This
399 always resolves those client ID's it does not know yet from the server
400 so this function might take a while. The `client_id_list' is a list
401 of ID Payloads added one after other. JOIN command reply and USERS
402 command reply for example returns this sort of list. The `completion'
403 will be called after the entries are available. */
405 void silc_client_get_clients_by_list(SilcClient client,
406 SilcClientConnection conn,
407 SilcUInt32 list_count,
408 SilcBuffer client_id_list,
409 SilcGetClientCallback completion,
412 SilcIDCacheEntry id_cache = NULL;
414 unsigned char **res_argv = NULL;
415 SilcUInt32 *res_argv_lens = NULL, *res_argv_types = NULL, res_argc = 0;
416 GetClientsByListInternal in;
417 bool wait_res = FALSE;
419 assert(client && conn && client_id_list);
421 SILC_LOG_DEBUG(("Start"));
423 in = silc_calloc(1, sizeof(*in));
426 in->list_count = list_count;
427 in->client_id_list = silc_buffer_copy(client_id_list);
428 in->completion = completion;
429 in->context = context;
431 for (i = 0; i < list_count; i++) {
433 SilcClientID *client_id;
434 SilcClientEntry entry;
438 SILC_GET16_MSB(idp_len, client_id_list->data + 2);
440 client_id = silc_id_payload_parse_id(client_id_list->data, idp_len, NULL);
442 silc_buffer_pull(client_id_list, idp_len);
446 /* Check if we have this client cached already. */
448 silc_idcache_find_by_id_one_ext(conn->internal->client_cache,
449 (void *)client_id, NULL, NULL,
450 silc_hash_client_id_compare, NULL,
453 /* If we don't have the entry or it has incomplete info, then resolve
454 it from the server. */
455 if (!ret || !((SilcClientEntry)id_cache->context)->nickname) {
456 entry = ret ? (SilcClientEntry)id_cache->context : NULL;
459 if (entry->status & SILC_CLIENT_STATUS_RESOLVING) {
460 /* Attach to this resolving and wait until it finishes */
461 silc_client_command_pending(
462 conn, SILC_COMMAND_NONE,
463 entry->resolve_cmd_ident,
464 silc_client_command_get_clients_list_callback,
469 silc_free(client_id);
470 silc_buffer_pull(client_id_list, idp_len);
474 entry->status |= SILC_CLIENT_STATUS_RESOLVING;
475 entry->resolve_cmd_ident = conn->cmd_ident + 1;
478 /* No we don't have it, query it from the server. Assemble argument
479 table that will be sent for the IDENTIFY command later. */
480 res_argv = silc_realloc(res_argv, sizeof(*res_argv) *
482 res_argv_lens = silc_realloc(res_argv_lens, sizeof(*res_argv_lens) *
484 res_argv_types = silc_realloc(res_argv_types, sizeof(*res_argv_types) *
486 res_argv[res_argc] = client_id_list->data;
487 res_argv_lens[res_argc] = idp_len;
488 res_argv_types[res_argc] = res_argc + 5;
492 silc_free(client_id);
493 silc_buffer_pull(client_id_list, idp_len);
496 silc_buffer_push(client_id_list, client_id_list->data -
497 client_id_list->head);
499 /* Query the client information from server if the list included clients
500 that we don't know about. */
504 /* Send the IDENTIFY command to server */
505 res_cmd = silc_command_payload_encode(SILC_COMMAND_IDENTIFY,
506 res_argc, res_argv, res_argv_lens,
507 res_argv_types, ++conn->cmd_ident);
508 silc_client_packet_send(client, conn->sock, SILC_PACKET_COMMAND,
509 NULL, 0, NULL, NULL, res_cmd->data, res_cmd->len,
512 /* Register our own command reply for this command */
513 silc_client_command_register(client, SILC_COMMAND_IDENTIFY, NULL, NULL,
514 silc_client_command_reply_identify_i, 0,
517 /* Process the applications request after reply has been received */
518 silc_client_command_pending(conn, SILC_COMMAND_IDENTIFY, conn->cmd_ident,
519 silc_client_command_get_clients_list_callback,
523 silc_buffer_free(res_cmd);
525 silc_free(res_argv_lens);
526 silc_free(res_argv_types);
533 /* We have the clients in cache, get them and call the completion */
534 silc_client_command_get_clients_list_callback((void *)in, NULL);
539 SilcClientConnection conn;
540 SilcChannelID channel_id;
541 SilcGetClientCallback completion;
544 } *GetClientsByChannelInternal;
546 SILC_CLIENT_CMD_FUNC(get_clients_by_channel_cb)
548 GetClientsByChannelInternal i = context;
549 SilcClientEntry *clients = NULL;
550 SilcUInt32 clients_count = 0;
552 SilcChannelEntry channel;
553 SilcHashTableList htl;
556 channel = silc_client_get_channel_by_id(i->client, i->conn, &i->channel_id);
557 if (channel && !silc_hash_table_count(channel->user_list)) {
558 clients = silc_calloc(silc_hash_table_count(channel->user_list),
560 silc_hash_table_list(channel->user_list, &htl);
561 while (silc_hash_table_get(&htl, NULL, (void *)&chu))
562 clients[clients_count++] = chu->client;
563 silc_hash_table_list_reset(&htl);
568 i->completion(i->client, i->conn, clients, clients_count, i->context);
571 i->completion(i->client, i->conn, NULL, 0, i->context);
577 /* Gets client entries by the channel entry indicated by `channel'. Thus,
578 it resolves the clients currently on that channel. */
580 void silc_client_get_clients_by_channel(SilcClient client,
581 SilcClientConnection conn,
582 SilcChannelEntry channel,
583 SilcGetClientCallback completion,
586 GetClientsByChannelInternal in;
587 SilcHashTableList htl;
589 SilcClientEntry entry;
590 unsigned char **res_argv = NULL;
591 SilcUInt32 *res_argv_lens = NULL, *res_argv_types = NULL, res_argc = 0;
593 bool wait_res = FALSE;
595 assert(client && conn && channel);
597 SILC_LOG_DEBUG(("Start"));
599 in = silc_calloc(1, sizeof(*in));
602 in->channel_id = *channel->id;
603 in->completion = completion;
604 in->context = context;
606 /* If user list does not exist, send USERS command. */
607 if (!channel->user_list || !silc_hash_table_count(channel->user_list)) {
608 SILC_LOG_DEBUG(("Sending USERS"));
609 silc_client_command_register(client, SILC_COMMAND_USERS, NULL, NULL,
610 silc_client_command_reply_users_i, 0,
612 silc_client_command_send(client, conn, SILC_COMMAND_USERS,
613 conn->cmd_ident, 1, 2, channel->channel_name,
614 strlen(channel->channel_name));
615 silc_client_command_pending(conn, SILC_COMMAND_USERS, conn->cmd_ident,
616 silc_client_command_get_clients_by_channel_cb,
621 silc_hash_table_list(channel->user_list, &htl);
622 while (silc_hash_table_get(&htl, NULL, (void *)&chu)) {
625 /* If the entry has incomplete info, then resolve it from the server. */
626 if (!entry->nickname || !entry->realname) {
627 if (entry->status & SILC_CLIENT_STATUS_RESOLVING) {
628 /* Attach to this resolving and wait until it finishes */
629 silc_client_command_pending(
630 conn, SILC_COMMAND_NONE,
631 entry->resolve_cmd_ident,
632 silc_client_command_get_clients_by_channel_cb,
638 entry->status |= SILC_CLIENT_STATUS_RESOLVING;
639 entry->resolve_cmd_ident = conn->cmd_ident + 1;
641 idp = silc_id_payload_encode(entry->id, SILC_ID_CLIENT);
643 /* No we don't have it, query it from the server. Assemble argument
644 table that will be sent for the WHOIS command later. */
645 res_argv = silc_realloc(res_argv, sizeof(*res_argv) *
647 res_argv_lens = silc_realloc(res_argv_lens, sizeof(*res_argv_lens) *
649 res_argv_types = silc_realloc(res_argv_types, sizeof(*res_argv_types) *
651 res_argv[res_argc] = silc_memdup(idp->data, idp->len);
652 res_argv_lens[res_argc] = idp->len;
653 res_argv_types[res_argc] = res_argc + 4;
656 silc_buffer_free(idp);
659 silc_hash_table_list_reset(&htl);
661 /* Query the client information from server if the list included clients
662 that we don't know about. */
666 /* Send the WHOIS command to server */
667 res_cmd = silc_command_payload_encode(SILC_COMMAND_WHOIS,
668 res_argc, res_argv, res_argv_lens,
669 res_argv_types, ++conn->cmd_ident);
670 silc_client_packet_send(client, conn->sock, SILC_PACKET_COMMAND,
671 NULL, 0, NULL, NULL, res_cmd->data, res_cmd->len,
674 /* Register our own command reply for this command */
675 silc_client_command_register(client, SILC_COMMAND_WHOIS, NULL, NULL,
676 silc_client_command_reply_whois_i, 0,
679 /* Process the applications request after reply has been received */
680 silc_client_command_pending(
681 conn, SILC_COMMAND_WHOIS, conn->cmd_ident,
682 silc_client_command_get_clients_by_channel_cb,
686 silc_buffer_free(res_cmd);
688 silc_free(res_argv_lens);
689 silc_free(res_argv_types);
696 /* We have the clients in cache, get them and call the completion */
697 silc_client_command_get_clients_by_channel_cb((void *)in, NULL);
700 /* Finds entry for client by the client's ID. Returns the entry or NULL
701 if the entry was not found. */
703 SilcClientEntry silc_client_get_client_by_id(SilcClient client,
704 SilcClientConnection conn,
705 SilcClientID *client_id)
707 SilcIDCacheEntry id_cache;
709 assert(client && conn);
713 SILC_LOG_DEBUG(("Finding client by ID (%s)",
714 silc_id_render(client_id, SILC_ID_CLIENT)));
716 /* Find ID from cache */
717 if (!silc_idcache_find_by_id_one_ext(conn->internal->client_cache,
718 (void *)client_id, NULL, NULL,
719 silc_hash_client_id_compare, NULL,
723 SILC_LOG_DEBUG(("Found"));
725 return (SilcClientEntry)id_cache->context;
730 SilcClientConnection conn;
731 SilcClientID *client_id;
732 SilcGetClientCallback completion;
734 } *GetClientByIDInternal;
736 SILC_CLIENT_CMD_FUNC(get_client_by_id_callback)
738 GetClientByIDInternal i = (GetClientByIDInternal)context;
739 SilcClientEntry entry;
742 entry = silc_client_get_client_by_id(i->client, i->conn, i->client_id);
745 i->completion(i->client, i->conn, &entry, 1, i->context);
748 i->completion(i->client, i->conn, NULL, 0, i->context);
751 silc_free(i->client_id);
755 /* Same as above but will always resolve the information from the server.
756 Use this only if you know that you don't have the entry and the only
757 thing you know about the client is its ID. */
759 void silc_client_get_client_by_id_resolve(SilcClient client,
760 SilcClientConnection conn,
761 SilcClientID *client_id,
762 SilcBuffer attributes,
763 SilcGetClientCallback completion,
767 GetClientByIDInternal i = silc_calloc(1, sizeof(*i));
769 assert(client && conn && client_id);
771 SILC_LOG_DEBUG(("Start"));
775 i->client_id = silc_id_dup(client_id, SILC_ID_CLIENT);
776 i->completion = completion;
777 i->context = context;
779 /* Register our own command reply for this command */
780 silc_client_command_register(client, SILC_COMMAND_WHOIS, NULL, NULL,
781 silc_client_command_reply_whois_i, 0,
784 /* Send the command */
785 idp = silc_id_payload_encode(client_id, SILC_ID_CLIENT);
786 silc_client_command_send(client, conn, SILC_COMMAND_WHOIS, conn->cmd_ident,
787 2, 3, attributes ? attributes->data : NULL,
788 attributes ? attributes->len : 0,
789 4, idp->data, idp->len);
790 silc_buffer_free(idp);
792 /* Add pending callback */
793 silc_client_command_pending(conn, SILC_COMMAND_WHOIS, conn->cmd_ident,
794 silc_client_command_get_client_by_id_callback,
799 /******************************************************************************
801 Client, Channel and Server entry manipulation
803 ******************************************************************************/
806 /* Creates new client entry and adds it to the ID cache. Returns pointer
810 silc_client_add_client(SilcClient client, SilcClientConnection conn,
811 char *nickname, char *username,
812 char *userinfo, SilcClientID *id, SilcUInt32 mode)
814 SilcClientEntry client_entry;
817 SILC_LOG_DEBUG(("Start"));
819 /* Save the client infos */
820 client_entry = silc_calloc(1, sizeof(*client_entry));
821 client_entry->id = id;
822 client_entry->valid = TRUE;
823 silc_parse_userfqdn(nickname, &nick, &client_entry->server);
824 silc_parse_userfqdn(username, &client_entry->username,
825 &client_entry->hostname);
827 client_entry->realname = strdup(userinfo);
828 client_entry->mode = mode;
830 client_entry->nickname = strdup(nick);
831 client_entry->channels = silc_hash_table_alloc(1, silc_hash_ptr, NULL, NULL,
832 NULL, NULL, NULL, TRUE);
834 /* Format the nickname */
835 silc_client_nickname_format(client, conn, client_entry);
837 /* Add client to cache, the non-formatted nickname is saved to cache */
838 if (!silc_idcache_add(conn->internal->client_cache, nick, client_entry->id,
839 (void *)client_entry, 0, NULL)) {
840 silc_free(client_entry->nickname);
841 silc_free(client_entry->username);
842 silc_free(client_entry->hostname);
843 silc_free(client_entry->server);
844 silc_hash_table_free(client_entry->channels);
845 silc_free(client_entry);
852 /* Updates the `client_entry' with the new information sent as argument. */
854 void silc_client_update_client(SilcClient client,
855 SilcClientConnection conn,
856 SilcClientEntry client_entry,
857 const char *nickname,
858 const char *username,
859 const char *userinfo,
864 SILC_LOG_DEBUG(("Start"));
866 if ((!client_entry->username || !client_entry->hostname) && username) {
867 silc_free(client_entry->username);
868 silc_free(client_entry->hostname);
869 client_entry->username = NULL;
870 client_entry->hostname = NULL;
871 silc_parse_userfqdn(username, &client_entry->username,
872 &client_entry->hostname);
874 if (!client_entry->realname && userinfo)
875 client_entry->realname = strdup(userinfo);
876 if (!client_entry->nickname && nickname) {
877 silc_parse_userfqdn(nickname, &nick, &client_entry->server);
878 client_entry->nickname = strdup(nick);
879 silc_client_nickname_format(client, conn, client_entry);
881 client_entry->mode = mode;
884 /* Remove the old cache entry and create a new one */
885 silc_idcache_del_by_context(conn->internal->client_cache, client_entry);
886 silc_idcache_add(conn->internal->client_cache, nick, client_entry->id,
887 client_entry, 0, NULL);
891 /* Deletes the client entry and frees all memory. */
893 void silc_client_del_client_entry(SilcClient client,
894 SilcClientConnection conn,
895 SilcClientEntry client_entry)
897 SILC_LOG_DEBUG(("Start"));
899 silc_free(client_entry->nickname);
900 silc_free(client_entry->username);
901 silc_free(client_entry->realname);
902 silc_free(client_entry->hostname);
903 silc_free(client_entry->server);
904 silc_free(client_entry->id);
905 silc_free(client_entry->fingerprint);
906 silc_hash_table_free(client_entry->channels);
907 if (client_entry->send_key)
908 silc_cipher_free(client_entry->send_key);
909 if (client_entry->receive_key)
910 silc_cipher_free(client_entry->receive_key);
911 silc_free(client_entry->key);
912 silc_client_ftp_session_free_client(conn, client_entry);
913 if (client_entry->ke)
914 silc_client_abort_key_agreement(client, conn, client_entry);
915 silc_free(client_entry);
918 /* Removes client from the cache by the client entry. */
920 bool silc_client_del_client(SilcClient client, SilcClientConnection conn,
921 SilcClientEntry client_entry)
923 bool ret = silc_idcache_del_by_context(conn->internal->client_cache,
927 /* Remove from channels */
928 silc_client_remove_from_channels(client, conn, client_entry);
930 /* Free the client entry data */
931 silc_client_del_client_entry(client, conn, client_entry);
937 /* Add new channel entry to the ID Cache */
939 SilcChannelEntry silc_client_add_channel(SilcClient client,
940 SilcClientConnection conn,
941 const char *channel_name,
943 SilcChannelID *channel_id)
945 SilcChannelEntry channel;
947 SILC_LOG_DEBUG(("Start"));
949 channel = silc_calloc(1, sizeof(*channel));
950 channel->channel_name = strdup(channel_name);
951 channel->id = channel_id;
952 channel->mode = mode;
953 channel->user_list = silc_hash_table_alloc(1, silc_hash_ptr, NULL, NULL,
954 NULL, NULL, NULL, TRUE);
956 /* Put it to the ID cache */
957 if (!silc_idcache_add(conn->internal->channel_cache, channel->channel_name,
958 (void *)channel->id, (void *)channel, 0, NULL)) {
959 silc_free(channel->channel_name);
960 silc_hash_table_free(channel->user_list);
968 /* Foreach callbcak to free all users from the channel when deleting a
971 static void silc_client_del_channel_foreach(void *key, void *context,
974 SilcChannelUser chu = (SilcChannelUser)context;
976 SILC_LOG_DEBUG(("Start"));
978 /* Remove the context from the client's channel hash table as that
979 table and channel's user_list hash table share this same context. */
980 silc_hash_table_del(chu->client->channels, chu->channel);
984 /* Removes channel from the cache by the channel entry. */
986 bool silc_client_del_channel(SilcClient client, SilcClientConnection conn,
987 SilcChannelEntry channel)
989 bool ret = silc_idcache_del_by_context(conn->internal->channel_cache,
992 SILC_LOG_DEBUG(("Start"));
994 /* Free all client entrys from the users list. The silc_hash_table_free
995 will free all the entries so they are not freed at the foreach
997 silc_hash_table_foreach(channel->user_list, silc_client_del_channel_foreach,
999 silc_hash_table_free(channel->user_list);
1001 silc_free(channel->channel_name);
1002 silc_free(channel->topic);
1003 silc_free(channel->id);
1004 silc_free(channel->key);
1005 if (channel->channel_key)
1006 silc_cipher_free(channel->channel_key);
1008 silc_hmac_free(channel->hmac);
1009 if (channel->old_channel_keys) {
1011 silc_dlist_start(channel->old_channel_keys);
1012 while ((key = silc_dlist_get(channel->old_channel_keys)) != SILC_LIST_END)
1013 silc_cipher_free(key);
1014 silc_dlist_uninit(channel->old_channel_keys);
1016 if (channel->old_hmacs) {
1018 silc_dlist_start(channel->old_hmacs);
1019 while ((hmac = silc_dlist_get(channel->old_hmacs)) != SILC_LIST_END)
1020 silc_hmac_free(hmac);
1021 silc_dlist_uninit(channel->old_hmacs);
1023 silc_schedule_task_del_by_context(conn->client->schedule, channel);
1024 silc_client_del_channel_private_keys(client, conn, channel);
1029 /* Replaces the channel ID of the `channel' to `new_id'. Returns FALSE
1030 if the ID could not be changed. */
1032 bool silc_client_replace_channel_id(SilcClient client,
1033 SilcClientConnection conn,
1034 SilcChannelEntry channel,
1035 SilcChannelID *new_id)
1040 SILC_LOG_DEBUG(("Old Channel ID id(%s)",
1041 silc_id_render(channel->id, SILC_ID_CHANNEL)));
1042 SILC_LOG_DEBUG(("New Channel ID id(%s)",
1043 silc_id_render(new_id, SILC_ID_CHANNEL)));
1045 silc_idcache_del_by_id(conn->internal->channel_cache, channel->id);
1046 silc_free(channel->id);
1047 channel->id = new_id;
1048 return silc_idcache_add(conn->internal->channel_cache,
1049 channel->channel_name,
1050 (void *)channel->id, (void *)channel, 0, NULL);
1054 /* Finds entry for channel by the channel name. Returns the entry or NULL
1055 if the entry was not found. It is found only if the client is joined
1058 SilcChannelEntry silc_client_get_channel(SilcClient client,
1059 SilcClientConnection conn,
1062 SilcIDCacheEntry id_cache;
1063 SilcChannelEntry entry;
1065 assert(client && conn);
1069 SILC_LOG_DEBUG(("Start"));
1071 if (!silc_idcache_find_by_name_one(conn->internal->channel_cache, channel,
1075 entry = (SilcChannelEntry)id_cache->context;
1077 SILC_LOG_DEBUG(("Found"));
1082 /* Finds entry for channel by the channel ID. Returns the entry or NULL
1083 if the entry was not found. It is found only if the client is joined
1086 SilcChannelEntry silc_client_get_channel_by_id(SilcClient client,
1087 SilcClientConnection conn,
1088 SilcChannelID *channel_id)
1090 SilcIDCacheEntry id_cache;
1091 SilcChannelEntry entry;
1093 assert(client && conn);
1097 SILC_LOG_DEBUG(("Start"));
1099 if (!silc_idcache_find_by_id_one(conn->internal->channel_cache, channel_id,
1103 entry = (SilcChannelEntry)id_cache->context;
1105 SILC_LOG_DEBUG(("Found"));
1112 SilcClientConnection conn;
1114 SilcChannelID *channel_id;
1117 SilcGetChannelCallback completion;
1119 } *GetChannelInternal;
1121 SILC_CLIENT_CMD_FUNC(get_channel_resolve_callback)
1123 GetChannelInternal i = (GetChannelInternal)context;
1124 SilcChannelEntry entry;
1126 SILC_LOG_DEBUG(("Start"));
1128 /* Get the channel */
1129 entry = silc_client_get_channel(i->client, i->conn, i->u.channel_name);
1131 i->completion(i->client, i->conn, &entry, 1, i->context);
1133 i->completion(i->client, i->conn, NULL, 0, i->context);
1136 silc_free(i->u.channel_name);
1140 /* Resolves channel entry from the server by the channel name. */
1142 void silc_client_get_channel_resolve(SilcClient client,
1143 SilcClientConnection conn,
1145 SilcGetChannelCallback completion,
1148 GetChannelInternal i = silc_calloc(1, sizeof(*i));
1150 assert(client && conn && channel_name);
1152 SILC_LOG_DEBUG(("Start"));
1156 i->u.channel_name = strdup(channel_name);
1157 i->completion = completion;
1158 i->context = context;
1160 /* Register our own command reply for this command */
1161 silc_client_command_register(client, SILC_COMMAND_IDENTIFY, NULL, NULL,
1162 silc_client_command_reply_identify_i, 0,
1165 /* Send the command */
1166 silc_client_command_send(client, conn, SILC_COMMAND_IDENTIFY,
1168 1, 3, channel_name, strlen(channel_name));
1170 /* Add pending callback */
1171 silc_client_command_pending(conn, SILC_COMMAND_IDENTIFY, conn->cmd_ident,
1172 silc_client_command_get_channel_resolve_callback,
1176 SILC_CLIENT_CMD_FUNC(get_channel_by_id_callback)
1178 GetChannelInternal i = (GetChannelInternal)context;
1179 SilcChannelEntry entry;
1181 SILC_LOG_DEBUG(("Start"));
1183 /* Get the channel */
1184 entry = silc_client_get_channel_by_id(i->client, i->conn, i->u.channel_id);
1186 i->completion(i->client, i->conn, &entry, 1, i->context);
1188 i->completion(i->client, i->conn, NULL, 0, i->context);
1191 silc_free(i->u.channel_id);
1195 /* Resolves channel information from the server by the channel ID. */
1197 void silc_client_get_channel_by_id_resolve(SilcClient client,
1198 SilcClientConnection conn,
1199 SilcChannelID *channel_id,
1200 SilcGetChannelCallback completion,
1204 GetChannelInternal i = silc_calloc(1, sizeof(*i));
1206 assert(client && conn && channel_id);
1208 SILC_LOG_DEBUG(("Start"));
1212 i->u.channel_id = silc_id_dup(channel_id, SILC_ID_CHANNEL);
1213 i->completion = completion;
1214 i->context = context;
1216 /* Register our own command reply for this command */
1217 silc_client_command_register(client, SILC_COMMAND_IDENTIFY, NULL, NULL,
1218 silc_client_command_reply_identify_i, 0,
1221 /* Send the command */
1222 idp = silc_id_payload_encode(channel_id, SILC_ID_CHANNEL);
1223 silc_client_command_send(client, conn, SILC_COMMAND_IDENTIFY,
1225 1, 5, idp->data, idp->len);
1226 silc_buffer_free(idp);
1228 /* Add pending callback */
1229 silc_client_command_pending(conn, SILC_COMMAND_IDENTIFY, conn->cmd_ident,
1230 silc_client_command_get_channel_by_id_callback,
1234 /* Finds entry for server by the server name. */
1236 SilcServerEntry silc_client_get_server(SilcClient client,
1237 SilcClientConnection conn,
1240 SilcIDCacheEntry id_cache;
1241 SilcServerEntry entry;
1243 assert(client && conn);
1247 SILC_LOG_DEBUG(("Start"));
1249 if (!silc_idcache_find_by_name_one(conn->internal->server_cache,
1250 server_name, &id_cache))
1253 entry = (SilcServerEntry)id_cache->context;
1258 /* Finds entry for server by the server ID. */
1260 SilcServerEntry silc_client_get_server_by_id(SilcClient client,
1261 SilcClientConnection conn,
1262 SilcServerID *server_id)
1264 SilcIDCacheEntry id_cache;
1265 SilcServerEntry entry;
1267 assert(client && conn);
1271 SILC_LOG_DEBUG(("Start"));
1273 if (!silc_idcache_find_by_id_one(conn->internal->server_cache,
1274 (void *)server_id, &id_cache))
1277 entry = (SilcServerEntry)id_cache->context;
1282 /* Add new server entry */
1284 SilcServerEntry silc_client_add_server(SilcClient client,
1285 SilcClientConnection conn,
1286 const char *server_name,
1287 const char *server_info,
1288 SilcServerID *server_id)
1290 SilcServerEntry server_entry;
1292 SILC_LOG_DEBUG(("Start"));
1294 server_entry = silc_calloc(1, sizeof(*server_entry));
1295 if (!server_entry || !server_id)
1298 server_entry->server_id = server_id;
1300 server_entry->server_name = strdup(server_name);
1302 server_entry->server_info = strdup(server_info);
1304 /* Add server to cache */
1305 if (!silc_idcache_add(conn->internal->server_cache,
1306 server_entry->server_name,
1307 server_entry->server_id, server_entry, 0, NULL)) {
1308 silc_free(server_entry->server_id);
1309 silc_free(server_entry->server_name);
1310 silc_free(server_entry->server_info);
1311 silc_free(server_entry);
1315 return server_entry;
1318 /* Removes server from the cache by the server entry. */
1320 bool silc_client_del_server(SilcClient client, SilcClientConnection conn,
1321 SilcServerEntry server)
1323 bool ret = silc_idcache_del_by_context(conn->internal->server_cache, server);
1324 silc_free(server->server_name);
1325 silc_free(server->server_info);
1326 silc_free(server->server_id);
1331 /* Updates the `server_entry' with the new information sent as argument. */
1333 void silc_client_update_server(SilcClient client,
1334 SilcClientConnection conn,
1335 SilcServerEntry server_entry,
1336 const char *server_name,
1337 const char *server_info)
1339 SILC_LOG_DEBUG(("Start"));
1341 if (server_name && (!server_entry->server_name ||
1342 strcmp(server_entry->server_name, server_name))) {
1344 silc_idcache_del_by_context(conn->internal->server_cache, server_entry);
1345 silc_free(server_entry->server_name);
1346 server_entry->server_name = strdup(server_name);
1347 silc_idcache_add(conn->internal->server_cache, server_entry->server_name,
1348 server_entry->server_id,
1349 server_entry, 0, NULL);
1352 if (server_info && (!server_entry->server_info ||
1353 strcmp(server_entry->server_info, server_info))) {
1354 silc_free(server_entry->server_info);
1355 server_entry->server_info = strdup(server_info);
1359 /* Formats the nickname of the client specified by the `client_entry'.
1360 If the format is specified by the application this will format the
1361 nickname and replace the old nickname in the client entry. If the
1362 format string is not specified then this function has no effect. */
1364 void silc_client_nickname_format(SilcClient client,
1365 SilcClientConnection conn,
1366 SilcClientEntry client_entry)
1369 char *newnick = NULL;
1370 int i, off = 0, len;
1372 SilcClientEntry *clients;
1373 SilcUInt32 clients_count = 0;
1374 SilcClientEntry unformatted = NULL;
1376 SILC_LOG_DEBUG(("Start"));
1378 if (!client->internal->params->nickname_format[0])
1381 if (!client_entry->nickname)
1384 /* Get all clients with same nickname. Do not perform the formatting
1385 if there aren't any clients with same nickname unless the application
1386 is forcing us to do so. */
1387 clients = silc_client_get_clients_local(client, conn,
1388 client_entry->nickname, NULL,
1390 if (!clients && !client->internal->params->nickname_force_format)
1395 for (i = 0; i < clients_count; i++) {
1396 if (clients[i]->valid && clients[i] != client_entry)
1398 if (clients[i]->valid && clients[i] != client_entry &&
1399 !strcasecmp(clients[i]->nickname, client_entry->nickname))
1402 if (!len || freebase)
1405 if (clients_count == 1)
1406 unformatted = clients[0];
1408 for (i = 0; i < clients_count; i++)
1409 if (!strncasecmp(clients[i]->nickname, client_entry->nickname,
1410 strlen(clients[i]->nickname)))
1411 unformatted = clients[i];
1413 /* If we are changing nickname of our local entry we'll enforce
1414 that we will always get the unformatted nickname. Give our
1415 format number to the one that is not formatted now. */
1416 if (unformatted && client_entry == conn->local_entry)
1417 client_entry = unformatted;
1419 cp = client->internal->params->nickname_format;
1429 if (!client_entry->nickname)
1431 len = strlen(client_entry->nickname);
1432 newnick = silc_realloc(newnick, sizeof(*newnick) * (off + len));
1433 memcpy(&newnick[off], client_entry->nickname, len);
1437 /* Stripped hostname */
1438 if (!client_entry->hostname)
1440 len = strcspn(client_entry->hostname, ".");
1441 i = strcspn(client_entry->hostname, "-");
1444 newnick = silc_realloc(newnick, sizeof(*newnick) * (off + len));
1445 memcpy(&newnick[off], client_entry->hostname, len);
1450 if (!client_entry->hostname)
1452 len = strlen(client_entry->hostname);
1453 newnick = silc_realloc(newnick, sizeof(*newnick) * (off + len));
1454 memcpy(&newnick[off], client_entry->hostname, len);
1458 /* Stripped server name */
1459 if (!client_entry->server)
1461 len = strcspn(client_entry->server, ".");
1462 newnick = silc_realloc(newnick, sizeof(*newnick) * (off + len));
1463 memcpy(&newnick[off], client_entry->server, len);
1467 /* Full server name */
1468 if (!client_entry->server)
1470 len = strlen(client_entry->server);
1471 newnick = silc_realloc(newnick, sizeof(*newnick) * (off + len));
1472 memcpy(&newnick[off], client_entry->server, len);
1476 /* Ascending number */
1481 if (clients_count == 1)
1484 for (i = 0; i < clients_count; i++) {
1485 if (strncasecmp(clients[i]->nickname, newnick, off))
1487 if (strlen(clients[i]->nickname) <= off)
1489 num = atoi(&clients[i]->nickname[off]);
1494 memset(tmp, 0, sizeof(tmp));
1495 snprintf(tmp, sizeof(tmp) - 1, "%d", ++max);
1497 newnick = silc_realloc(newnick, sizeof(*newnick) * (off + len));
1498 memcpy(&newnick[off], tmp, len);
1503 /* Some other character in the string */
1504 newnick = silc_realloc(newnick, sizeof(*newnick) * (off + 1));
1505 memcpy(&newnick[off], cp, 1);
1513 newnick = silc_realloc(newnick, sizeof(*newnick) * (off + 1));
1516 silc_free(client_entry->nickname);
1517 client_entry->nickname = newnick;