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,
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->topic);
967 silc_free(channel->id);
968 silc_free(channel->key);
969 if (channel->channel_key)
970 silc_cipher_free(channel->channel_key);
972 silc_hmac_free(channel->hmac);
973 if (channel->old_channel_keys) {
975 silc_dlist_start(channel->old_channel_keys);
976 while ((key = silc_dlist_get(channel->old_channel_keys)) != SILC_LIST_END)
977 silc_cipher_free(key);
978 silc_dlist_uninit(channel->old_channel_keys);
980 if (channel->old_hmacs) {
982 silc_dlist_start(channel->old_hmacs);
983 while ((hmac = silc_dlist_get(channel->old_hmacs)) != SILC_LIST_END)
984 silc_hmac_free(hmac);
985 silc_dlist_uninit(channel->old_hmacs);
987 silc_schedule_task_del_by_context(conn->client->schedule, channel);
988 silc_client_del_channel_private_keys(client, conn, channel);
993 /* Replaces the channel ID of the `channel' to `new_id'. Returns FALSE
994 if the ID could not be changed. */
996 bool silc_client_replace_channel_id(SilcClient client,
997 SilcClientConnection conn,
998 SilcChannelEntry channel,
999 SilcChannelID *new_id)
1004 SILC_LOG_DEBUG(("Old Channel ID id(%s)",
1005 silc_id_render(channel->id, SILC_ID_CHANNEL)));
1006 SILC_LOG_DEBUG(("New Channel ID id(%s)",
1007 silc_id_render(new_id, SILC_ID_CHANNEL)));
1009 silc_idcache_del_by_id(conn->internal->channel_cache, channel->id);
1010 silc_free(channel->id);
1011 channel->id = new_id;
1012 return silc_idcache_add(conn->internal->channel_cache,
1013 channel->channel_name,
1014 (void *)channel->id, (void *)channel, 0, NULL);
1018 /* Finds entry for channel by the channel name. Returns the entry or NULL
1019 if the entry was not found. It is found only if the client is joined
1022 SilcChannelEntry silc_client_get_channel(SilcClient client,
1023 SilcClientConnection conn,
1026 SilcIDCacheEntry id_cache;
1027 SilcChannelEntry entry;
1029 assert(client && conn);
1033 SILC_LOG_DEBUG(("Start"));
1035 if (!silc_idcache_find_by_name_one(conn->internal->channel_cache, channel,
1039 entry = (SilcChannelEntry)id_cache->context;
1041 SILC_LOG_DEBUG(("Found"));
1046 /* Finds entry for channel by the channel ID. Returns the entry or NULL
1047 if the entry was not found. It is found only if the client is joined
1050 SilcChannelEntry silc_client_get_channel_by_id(SilcClient client,
1051 SilcClientConnection conn,
1052 SilcChannelID *channel_id)
1054 SilcIDCacheEntry id_cache;
1055 SilcChannelEntry entry;
1057 assert(client && conn);
1061 SILC_LOG_DEBUG(("Start"));
1063 if (!silc_idcache_find_by_id_one(conn->internal->channel_cache, channel_id,
1067 entry = (SilcChannelEntry)id_cache->context;
1069 SILC_LOG_DEBUG(("Found"));
1076 SilcClientConnection conn;
1078 SilcChannelID *channel_id;
1081 SilcGetChannelCallback completion;
1083 } *GetChannelInternal;
1085 SILC_CLIENT_CMD_FUNC(get_channel_resolve_callback)
1087 GetChannelInternal i = (GetChannelInternal)context;
1088 SilcChannelEntry entry;
1090 SILC_LOG_DEBUG(("Start"));
1092 /* Get the channel */
1093 entry = silc_client_get_channel(i->client, i->conn, i->u.channel_name);
1095 i->completion(i->client, i->conn, &entry, 1, i->context);
1097 i->completion(i->client, i->conn, NULL, 0, i->context);
1100 silc_free(i->u.channel_name);
1104 /* Resolves channel entry from the server by the channel name. */
1106 void silc_client_get_channel_resolve(SilcClient client,
1107 SilcClientConnection conn,
1109 SilcGetChannelCallback completion,
1112 GetChannelInternal i = silc_calloc(1, sizeof(*i));
1114 assert(client && conn && channel_name);
1116 SILC_LOG_DEBUG(("Start"));
1120 i->u.channel_name = strdup(channel_name);
1121 i->completion = completion;
1122 i->context = context;
1124 /* Register our own command reply for this command */
1125 silc_client_command_register(client, SILC_COMMAND_IDENTIFY, NULL, NULL,
1126 silc_client_command_reply_identify_i, 0,
1129 /* Send the command */
1130 silc_client_command_send(client, conn, SILC_COMMAND_IDENTIFY,
1132 1, 3, channel_name, strlen(channel_name));
1134 /* Add pending callback */
1135 silc_client_command_pending(conn, SILC_COMMAND_IDENTIFY, conn->cmd_ident,
1136 silc_client_command_get_channel_resolve_callback,
1140 SILC_CLIENT_CMD_FUNC(get_channel_by_id_callback)
1142 GetChannelInternal i = (GetChannelInternal)context;
1143 SilcChannelEntry entry;
1145 SILC_LOG_DEBUG(("Start"));
1147 /* Get the channel */
1148 entry = silc_client_get_channel_by_id(i->client, i->conn, i->u.channel_id);
1150 i->completion(i->client, i->conn, &entry, 1, i->context);
1152 i->completion(i->client, i->conn, NULL, 0, i->context);
1155 silc_free(i->u.channel_id);
1159 /* Resolves channel information from the server by the channel ID. */
1161 void silc_client_get_channel_by_id_resolve(SilcClient client,
1162 SilcClientConnection conn,
1163 SilcChannelID *channel_id,
1164 SilcGetChannelCallback completion,
1168 GetChannelInternal i = silc_calloc(1, sizeof(*i));
1170 assert(client && conn && channel_id);
1172 SILC_LOG_DEBUG(("Start"));
1176 i->u.channel_id = silc_id_dup(channel_id, SILC_ID_CHANNEL);
1177 i->completion = completion;
1178 i->context = context;
1180 /* Register our own command reply for this command */
1181 silc_client_command_register(client, SILC_COMMAND_IDENTIFY, NULL, NULL,
1182 silc_client_command_reply_identify_i, 0,
1185 /* Send the command */
1186 idp = silc_id_payload_encode(channel_id, SILC_ID_CHANNEL);
1187 silc_client_command_send(client, conn, SILC_COMMAND_IDENTIFY,
1189 1, 5, idp->data, idp->len);
1190 silc_buffer_free(idp);
1192 /* Add pending callback */
1193 silc_client_command_pending(conn, SILC_COMMAND_IDENTIFY, conn->cmd_ident,
1194 silc_client_command_get_channel_by_id_callback,
1198 /* Finds entry for server by the server name. */
1200 SilcServerEntry silc_client_get_server(SilcClient client,
1201 SilcClientConnection conn,
1204 SilcIDCacheEntry id_cache;
1205 SilcServerEntry entry;
1207 assert(client && conn);
1211 SILC_LOG_DEBUG(("Start"));
1213 if (!silc_idcache_find_by_name_one(conn->internal->server_cache,
1214 server_name, &id_cache))
1217 entry = (SilcServerEntry)id_cache->context;
1222 /* Finds entry for server by the server ID. */
1224 SilcServerEntry silc_client_get_server_by_id(SilcClient client,
1225 SilcClientConnection conn,
1226 SilcServerID *server_id)
1228 SilcIDCacheEntry id_cache;
1229 SilcServerEntry entry;
1231 assert(client && conn);
1235 SILC_LOG_DEBUG(("Start"));
1237 if (!silc_idcache_find_by_id_one(conn->internal->server_cache,
1238 (void *)server_id, &id_cache))
1241 entry = (SilcServerEntry)id_cache->context;
1246 /* Add new server entry */
1248 SilcServerEntry silc_client_add_server(SilcClient client,
1249 SilcClientConnection conn,
1250 const char *server_name,
1251 const char *server_info,
1252 SilcServerID *server_id)
1254 SilcServerEntry server_entry;
1256 SILC_LOG_DEBUG(("Start"));
1258 server_entry = silc_calloc(1, sizeof(*server_entry));
1259 if (!server_entry || !server_id)
1262 server_entry->server_id = server_id;
1264 server_entry->server_name = strdup(server_name);
1266 server_entry->server_info = strdup(server_info);
1268 /* Add server to cache */
1269 if (!silc_idcache_add(conn->internal->server_cache,
1270 server_entry->server_name,
1271 server_entry->server_id, server_entry, 0, NULL)) {
1272 silc_free(server_entry->server_id);
1273 silc_free(server_entry->server_name);
1274 silc_free(server_entry->server_info);
1275 silc_free(server_entry);
1279 return server_entry;
1282 /* Removes server from the cache by the server entry. */
1284 bool silc_client_del_server(SilcClient client, SilcClientConnection conn,
1285 SilcServerEntry server)
1287 bool ret = silc_idcache_del_by_context(conn->internal->server_cache, server);
1288 silc_free(server->server_name);
1289 silc_free(server->server_info);
1290 silc_free(server->server_id);
1295 /* Updates the `server_entry' with the new information sent as argument. */
1297 void silc_client_update_server(SilcClient client,
1298 SilcClientConnection conn,
1299 SilcServerEntry server_entry,
1300 const char *server_name,
1301 const char *server_info)
1303 SILC_LOG_DEBUG(("Start"));
1305 if (server_name && (!server_entry->server_name ||
1306 strcmp(server_entry->server_name, server_name))) {
1308 silc_idcache_del_by_context(conn->internal->server_cache, server_entry);
1309 silc_free(server_entry->server_name);
1310 server_entry->server_name = strdup(server_name);
1311 silc_idcache_add(conn->internal->server_cache, server_entry->server_name,
1312 server_entry->server_id,
1313 server_entry, 0, NULL);
1316 if (server_info && (!server_entry->server_info ||
1317 strcmp(server_entry->server_info, server_info))) {
1318 silc_free(server_entry->server_info);
1319 server_entry->server_info = strdup(server_info);
1323 /* Formats the nickname of the client specified by the `client_entry'.
1324 If the format is specified by the application this will format the
1325 nickname and replace the old nickname in the client entry. If the
1326 format string is not specified then this function has no effect. */
1328 void silc_client_nickname_format(SilcClient client,
1329 SilcClientConnection conn,
1330 SilcClientEntry client_entry)
1333 char *newnick = NULL;
1334 int i, off = 0, len;
1336 SilcClientEntry *clients;
1337 SilcUInt32 clients_count = 0;
1338 SilcClientEntry unformatted = NULL;
1340 SILC_LOG_DEBUG(("Start"));
1342 if (!client->internal->params->nickname_format[0])
1345 if (!client_entry->nickname)
1348 /* Get all clients with same nickname. Do not perform the formatting
1349 if there aren't any clients with same nickname unless the application
1350 is forcing us to do so. */
1351 clients = silc_client_get_clients_local(client, conn,
1352 client_entry->nickname, NULL,
1354 if (!clients && !client->internal->params->nickname_force_format)
1359 for (i = 0; i < clients_count; i++) {
1360 if (clients[i]->valid && clients[i] != client_entry)
1362 if (clients[i]->valid && clients[i] != client_entry &&
1363 !strcasecmp(clients[i]->nickname, client_entry->nickname))
1366 if (!len || freebase)
1369 if (clients_count == 1)
1370 unformatted = clients[0];
1372 for (i = 0; i < clients_count; i++)
1373 if (!strncasecmp(clients[i]->nickname, client_entry->nickname,
1374 strlen(clients[i]->nickname)))
1375 unformatted = clients[i];
1377 /* If we are changing nickname of our local entry we'll enforce
1378 that we will always get the unformatted nickname. Give our
1379 format number to the one that is not formatted now. */
1380 if (unformatted && client_entry == conn->local_entry)
1381 client_entry = unformatted;
1383 cp = client->internal->params->nickname_format;
1393 if (!client_entry->nickname)
1395 len = strlen(client_entry->nickname);
1396 newnick = silc_realloc(newnick, sizeof(*newnick) * (off + len));
1397 memcpy(&newnick[off], client_entry->nickname, len);
1401 /* Stripped hostname */
1402 if (!client_entry->hostname)
1404 len = strcspn(client_entry->hostname, ".");
1405 i = strcspn(client_entry->hostname, "-");
1408 newnick = silc_realloc(newnick, sizeof(*newnick) * (off + len));
1409 memcpy(&newnick[off], client_entry->hostname, len);
1414 if (!client_entry->hostname)
1416 len = strlen(client_entry->hostname);
1417 newnick = silc_realloc(newnick, sizeof(*newnick) * (off + len));
1418 memcpy(&newnick[off], client_entry->hostname, len);
1422 /* Stripped server name */
1423 if (!client_entry->server)
1425 len = strcspn(client_entry->server, ".");
1426 newnick = silc_realloc(newnick, sizeof(*newnick) * (off + len));
1427 memcpy(&newnick[off], client_entry->server, len);
1431 /* Full server name */
1432 if (!client_entry->server)
1434 len = strlen(client_entry->server);
1435 newnick = silc_realloc(newnick, sizeof(*newnick) * (off + len));
1436 memcpy(&newnick[off], client_entry->server, len);
1440 /* Ascending number */
1445 if (clients_count == 1)
1448 for (i = 0; i < clients_count; i++) {
1449 if (strncasecmp(clients[i]->nickname, newnick, off))
1451 if (strlen(clients[i]->nickname) <= off)
1453 num = atoi(&clients[i]->nickname[off]);
1458 memset(tmp, 0, sizeof(tmp));
1459 snprintf(tmp, sizeof(tmp) - 1, "%d", ++max);
1461 newnick = silc_realloc(newnick, sizeof(*newnick) * (off + len));
1462 memcpy(&newnick[off], tmp, len);
1467 /* Some other character in the string */
1468 newnick = silc_realloc(newnick, sizeof(*newnick) * (off + 1));
1469 memcpy(&newnick[off], cp, 1);
1477 newnick = silc_realloc(newnick, sizeof(*newnick) * (off + 1));
1480 silc_free(client_entry->nickname);
1481 client_entry->nickname = newnick;