5 Author: Pekka Riikonen <priikone@silcnet.org>
7 Copyright (C) 2001 - 2005 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;
50 SilcBool found = FALSE;
53 assert(client && conn);
57 /* Normalize nickname for search */
58 nicknamec = silc_identifier_check(nickname, strlen(nickname),
59 SILC_STRING_UTF8, 128, NULL);
63 /* Find ID from cache */
64 if (!silc_idcache_find_by_name(conn->internal->client_cache, nicknamec,
70 if (!silc_idcache_list_count(list)) {
71 silc_idcache_list_free(list);
76 clients = silc_calloc(silc_idcache_list_count(list), sizeof(*clients));
77 *clients_count = silc_idcache_list_count(list);
80 /* Take all without any further checking */
81 silc_idcache_list_first(list, &id_cache);
83 clients[i++] = id_cache->context;
85 if (!silc_idcache_list_next(list, &id_cache))
89 /* Check multiple cache entries for match */
90 silc_idcache_list_first(list, &id_cache);
92 entry = (SilcClientEntry)id_cache->context;
93 if (!silc_utf8_strcasecmp(entry->nickname, format)) {
94 if (!silc_idcache_list_next(list, &id_cache)) {
101 clients[i++] = id_cache->context;
103 if (!silc_idcache_list_next(list, &id_cache))
108 silc_free(nicknamec);
111 silc_idcache_list_free(list);
124 /******************************************************************************
126 Client Resolving from Server
128 ******************************************************************************/
132 SilcClientConnection conn;
133 SilcGetClientCallback completion;
136 SilcClientEntry *clients;
137 SilcUInt32 clients_count;
138 } *GetClientInternal;
140 /* Completion for IDENTIFY */
142 SILC_CLIENT_CMD_FUNC(get_client_callback)
144 GetClientInternal i = (GetClientInternal)context;
145 SilcClientEntry *clients;
146 SilcUInt32 clients_count;
148 /* Get the clients */
149 clients = silc_client_get_clients_local(i->client, i->conn,
153 i->completion(i->client, i->conn, clients, clients_count, i->context);
156 i->completion(i->client, i->conn, NULL, 0, i->context);
159 silc_free(i->nickname);
163 /* Completion for WHOIS */
165 SILC_CLIENT_CMD_FUNC(get_client_callback_wc)
167 GetClientInternal i = (GetClientInternal)context;
168 SilcClientCommandReplyContext cmd = context2;
169 SilcClientID *client_id = NULL;
170 SilcClientEntry client_entry = NULL;
171 unsigned char *id_data;
174 /* Get the client entry just returned from server */
175 id_data = silc_argument_get_arg_type(cmd->args, 2, &len);
177 client_id = silc_id_payload_parse_id(id_data, len, NULL);
179 client_entry = silc_client_get_client_by_id(i->client,
182 if (!SILC_STATUS_IS_ERROR(cmd->status) &&
183 cmd->status != SILC_STATUS_OK &&
184 cmd->status != SILC_STATUS_LIST_END) {
185 silc_free(client_id);
189 i->completion(i->client, i->conn, i->clients, i->clients_count,
191 silc_free(client_id);
192 silc_free(i->clients);
193 silc_free(i->nickname);
198 /* Save the client */
199 i->clients = silc_realloc(i->clients,
200 (sizeof(*i->clients) * (i->clients_count + 1)));
201 i->clients[i->clients_count] = client_entry;
204 /* Return if more data is expected */
205 if (cmd->status != SILC_STATUS_OK &&
206 cmd->status != SILC_STATUS_LIST_END) {
207 silc_free(client_id);
211 i->completion(i->client, i->conn, i->clients, i->clients_count,
214 silc_free(client_id);
215 silc_free(i->clients);
216 silc_free(i->nickname);
220 /* Our own WHOIS reply processor. */
222 SILC_CLIENT_CMD_FUNC(get_client_callback_w)
224 SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
225 SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
227 SILC_LOG_DEBUG(("Start"));
229 if (!silc_command_get_status(cmd->payload, NULL, NULL)) {
230 if (SILC_STATUS_IS_ERROR(cmd->status))
232 if (cmd->status == SILC_STATUS_LIST_END)
237 /* Save WHOIS info */
238 silc_client_command_reply_whois_save(cmd, cmd->status, FALSE);
240 /* Call pending completion for each reply */
241 if (cmd->status != SILC_STATUS_OK &&
242 cmd->status != SILC_STATUS_LIST_END) {
243 if (cmd->callbacks[0].callback)
244 (*cmd->callbacks[0].callback)(cmd->callbacks[0].context, cmd);
245 silc_client_command_reply_free(cmd);
250 SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_WHOIS);
253 /* If we received notify for invalid ID we'll remove the ID if we
255 if (cmd->error == SILC_STATUS_ERR_NO_SUCH_CLIENT_ID) {
256 SilcClientEntry client_entry;
259 silc_argument_get_arg_type(silc_command_get_args(cmd->payload),
262 SilcClientID *client_id = silc_id_payload_parse_id(tmp, tmp_len, NULL);
264 client_entry = silc_client_get_client_by_id(cmd->client, conn,
267 silc_client_del_client(cmd->client, conn, client_entry);
268 silc_free(client_id);
273 /* Unregister this command reply */
274 silc_client_command_unregister(cmd->client, SILC_COMMAND_WHOIS,
275 NULL, silc_client_command_reply_whois_i,
277 silc_client_command_reply_free(cmd);
280 /* Finds client entry or entries by the `nickname' and `server'. The
281 completion callback will be called when the client entries has been found.
283 Note: this function is always asynchronous and resolves the client
284 information from the server. Thus, if you already know the client
285 information then use the silc_client_get_client_by_id function to
286 get the client entry since this function may be very slow and should
287 be used only to initially get the client entries. */
289 void silc_client_get_clients_i(SilcClient client,
290 SilcClientConnection conn,
292 const char *nickname,
294 SilcBuffer attributes,
295 SilcGetClientCallback completion,
300 char *userhost = NULL;
302 assert(client && conn);
304 if (!nickname && !attributes)
307 i = silc_calloc(1, sizeof(*i));
310 i->nickname = nickname ? strdup(nickname) : NULL;
311 i->completion = completion;
312 i->context = context;
314 if (nickname && server) {
315 len = strlen(nickname) + strlen(server) + 3;
316 userhost = silc_calloc(len, sizeof(*userhost));
317 silc_strncat(userhost, len, nickname, strlen(nickname));
318 silc_strncat(userhost, len, "@", 1);
319 silc_strncat(userhost, len, server, strlen(server));
320 } else if (nickname) {
321 userhost = silc_memdup(nickname, strlen(nickname));
324 /* Register our own command reply for this command */
325 if (command == SILC_COMMAND_IDENTIFY) {
326 silc_client_command_register(client, command, NULL, NULL,
327 silc_client_command_reply_identify_i, 0,
329 /* Send the command */
330 silc_client_command_send(client, conn, SILC_COMMAND_IDENTIFY,
331 conn->cmd_ident, 1, 1, userhost,
334 /* Add pending callback */
335 silc_client_command_pending(conn, command, conn->cmd_ident,
336 silc_client_command_get_client_callback,
339 silc_client_command_register(client, command, NULL, NULL,
340 silc_client_command_get_client_callback_w, 0,
342 /* Send the command */
343 silc_client_command_send(client, conn, command, conn->cmd_ident, 2,
344 1, userhost, userhost ? strlen(userhost) : 0,
345 3, attributes ? attributes->data : NULL,
346 attributes ? attributes->len : 0);
348 /* Add pending callback */
349 silc_client_command_pending(conn, command, conn->cmd_ident,
350 silc_client_command_get_client_callback_wc,
356 void silc_client_get_clients(SilcClient client,
357 SilcClientConnection conn,
358 const char *nickname,
360 SilcGetClientCallback completion,
363 silc_client_get_clients_i(client, conn, SILC_COMMAND_IDENTIFY,
364 nickname, server, NULL,
365 completion, context);
368 void silc_client_get_clients_whois(SilcClient client,
369 SilcClientConnection conn,
370 const char *nickname,
372 SilcBuffer attributes,
373 SilcGetClientCallback completion,
376 silc_client_get_clients_i(client, conn, SILC_COMMAND_WHOIS,
377 nickname, server, attributes,
378 completion, context);
381 /* The old style function to find client entry. This is used by the
382 library internally. If `query' is TRUE then the client information is
383 requested by the server. The pending command callback must be set
385 /* XXX This function should be removed */
387 SilcClientEntry silc_idlist_get_client(SilcClient client,
388 SilcClientConnection conn,
389 const char *nickname,
393 SilcIDCacheEntry id_cache;
394 SilcIDCacheList list = NULL;
395 SilcClientEntry entry = NULL;
398 SILC_LOG_DEBUG(("Start"));
400 /* Normalize nickname for search */
401 nicknamec = silc_identifier_check(nickname, strlen(nickname),
402 SILC_STRING_UTF8, 128, NULL);
406 /* Find ID from cache */
407 if (!silc_idcache_find_by_name(conn->internal->client_cache,
412 SILC_LOG_DEBUG(("Requesting Client ID from server"));
414 /* Register our own command reply for this command */
415 silc_client_command_register(client, SILC_COMMAND_IDENTIFY, NULL, NULL,
416 silc_client_command_reply_identify_i, 0,
419 /* Send the command */
420 silc_client_command_send(client, conn, SILC_COMMAND_IDENTIFY,
421 conn->cmd_ident, 1, 1, nickname,
425 silc_idcache_list_free(list);
427 silc_free(nicknamec);
431 silc_free(nicknamec);
436 /* Take first found cache entry */
437 if (!silc_idcache_list_first(list, &id_cache))
440 entry = (SilcClientEntry)id_cache->context;
442 /* Check multiple cache entries for match */
443 silc_idcache_list_first(list, &id_cache);
445 entry = (SilcClientEntry)id_cache->context;
447 if (!silc_utf8_strcasecmp(entry->nickname, format)) {
448 if (!silc_idcache_list_next(list, &id_cache)) {
460 /* If match weren't found, request it */
465 silc_free(nicknamec);
468 silc_idcache_list_free(list);
475 SilcClientConnection conn;
476 SilcUInt32 list_count;
477 SilcBuffer client_id_list;
478 SilcGetClientCallback completion;
481 } *GetClientsByListInternal;
483 SILC_CLIENT_CMD_FUNC(get_clients_list_callback)
485 GetClientsByListInternal i = (GetClientsByListInternal)context;
486 SilcIDCacheEntry id_cache = NULL;
487 SilcBuffer client_id_list = i->client_id_list;
488 SilcClientEntry *clients = NULL;
489 SilcUInt32 clients_count = 0;
490 SilcBool found = FALSE;
493 SILC_LOG_DEBUG(("Start"));
501 SILC_LOG_DEBUG(("Resolved all clients"));
503 clients = silc_calloc(i->list_count, sizeof(*clients));
505 for (c = 0; c < i->list_count; c++) {
507 SilcClientID *client_id;
510 SILC_GET16_MSB(idp_len, client_id_list->data + 2);
512 client_id = silc_id_payload_parse_id(client_id_list->data, idp_len, NULL);
514 silc_buffer_pull(client_id_list, idp_len);
518 /* Get the client entry */
519 if (silc_idcache_find_by_id_one_ext(i->conn->internal->client_cache,
522 silc_hash_client_id_compare, NULL,
524 clients[clients_count] = (SilcClientEntry)id_cache->context;
529 silc_free(client_id);
530 silc_buffer_pull(client_id_list, idp_len);
534 i->completion(i->client, i->conn, clients, clients_count, i->context);
537 i->completion(i->client, i->conn, NULL, 0, i->context);
540 if (i->client_id_list)
541 silc_buffer_free(i->client_id_list);
545 /* Gets client entries by the list of client ID's `client_id_list'. This
546 always resolves those client ID's it does not know yet from the server
547 so this function might take a while. The `client_id_list' is a list
548 of ID Payloads added one after other. JOIN command reply and USERS
549 command reply for example returns this sort of list. The `completion'
550 will be called after the entries are available. */
552 void silc_client_get_clients_by_list(SilcClient client,
553 SilcClientConnection conn,
554 SilcUInt32 list_count,
555 SilcBuffer client_id_list,
556 SilcGetClientCallback completion,
559 SilcIDCacheEntry id_cache = NULL;
561 unsigned char **res_argv = NULL;
562 SilcUInt32 *res_argv_lens = NULL, *res_argv_types = NULL, res_argc = 0;
563 GetClientsByListInternal in;
564 SilcBool wait_res = FALSE;
566 assert(client && conn && client_id_list);
568 SILC_LOG_DEBUG(("Start"));
570 in = silc_calloc(1, sizeof(*in));
573 in->list_count = list_count;
574 in->client_id_list = silc_buffer_copy(client_id_list);
575 in->completion = completion;
576 in->context = context;
578 for (i = 0; i < list_count; i++) {
580 SilcClientID *client_id;
581 SilcClientEntry entry;
585 SILC_GET16_MSB(idp_len, client_id_list->data + 2);
587 client_id = silc_id_payload_parse_id(client_id_list->data, idp_len, NULL);
589 silc_buffer_pull(client_id_list, idp_len);
593 /* Check if we have this client cached already. */
595 silc_idcache_find_by_id_one_ext(conn->internal->client_cache,
596 (void *)client_id, NULL, NULL,
597 silc_hash_client_id_compare, NULL,
600 /* If we don't have the entry or it has incomplete info, then resolve
601 it from the server. */
602 if (!ret || !((SilcClientEntry)id_cache->context)->nickname) {
603 entry = ret ? (SilcClientEntry)id_cache->context : NULL;
606 if (entry->status & SILC_CLIENT_STATUS_RESOLVING) {
607 /* Attach to this resolving and wait until it finishes */
608 silc_client_command_pending(
609 conn, SILC_COMMAND_NONE,
610 entry->resolve_cmd_ident,
611 silc_client_command_get_clients_list_callback,
616 silc_free(client_id);
617 silc_buffer_pull(client_id_list, idp_len);
621 entry->status |= SILC_CLIENT_STATUS_RESOLVING;
622 entry->resolve_cmd_ident = conn->cmd_ident + 1;
625 /* No we don't have it, query it from the server. Assemble argument
626 table that will be sent for the IDENTIFY command later. */
627 res_argv = silc_realloc(res_argv, sizeof(*res_argv) *
629 res_argv_lens = silc_realloc(res_argv_lens, sizeof(*res_argv_lens) *
631 res_argv_types = silc_realloc(res_argv_types, sizeof(*res_argv_types) *
633 res_argv[res_argc] = client_id_list->data;
634 res_argv_lens[res_argc] = idp_len;
635 res_argv_types[res_argc] = res_argc + 5;
639 silc_free(client_id);
640 silc_buffer_pull(client_id_list, idp_len);
643 silc_buffer_push(client_id_list, client_id_list->data -
644 client_id_list->head);
646 /* Query the client information from server if the list included clients
647 that we don't know about. */
651 /* Send the IDENTIFY command to server */
652 res_cmd = silc_command_payload_encode(SILC_COMMAND_IDENTIFY,
653 res_argc, res_argv, res_argv_lens,
654 res_argv_types, ++conn->cmd_ident);
655 silc_client_packet_send(client, conn->sock, SILC_PACKET_COMMAND,
656 NULL, 0, NULL, NULL, res_cmd->data, res_cmd->len,
659 /* Register our own command reply for this command */
660 silc_client_command_register(client, SILC_COMMAND_IDENTIFY, NULL, NULL,
661 silc_client_command_reply_identify_i, 0,
664 /* Process the applications request after reply has been received */
665 silc_client_command_pending(conn, SILC_COMMAND_IDENTIFY, conn->cmd_ident,
666 silc_client_command_get_clients_list_callback,
670 silc_buffer_free(res_cmd);
672 silc_free(res_argv_lens);
673 silc_free(res_argv_types);
680 /* We have the clients in cache, get them and call the completion */
681 silc_client_command_get_clients_list_callback((void *)in, NULL);
686 SilcClientConnection conn;
687 SilcChannelID channel_id;
688 SilcGetClientCallback completion;
691 } *GetClientsByChannelInternal;
693 SILC_CLIENT_CMD_FUNC(get_clients_by_channel_cb)
695 GetClientsByChannelInternal i = context;
696 SilcClientEntry *clients = NULL;
697 SilcUInt32 clients_count = 0;
698 SilcBool found = FALSE;
699 SilcChannelEntry channel;
700 SilcHashTableList htl;
709 channel = silc_client_get_channel_by_id(i->client, i->conn, &i->channel_id);
710 if (channel && !silc_hash_table_count(channel->user_list)) {
711 clients = silc_calloc(silc_hash_table_count(channel->user_list),
713 silc_hash_table_list(channel->user_list, &htl);
714 while (silc_hash_table_get(&htl, NULL, (void *)&chu))
715 clients[clients_count++] = chu->client;
716 silc_hash_table_list_reset(&htl);
721 i->completion(i->client, i->conn, clients, clients_count, i->context);
724 i->completion(i->client, i->conn, NULL, 0, i->context);
730 /* Gets client entries by the channel entry indicated by `channel'. Thus,
731 it resolves the clients currently on that channel. */
733 void silc_client_get_clients_by_channel(SilcClient client,
734 SilcClientConnection conn,
735 SilcChannelEntry channel,
736 SilcGetClientCallback completion,
739 GetClientsByChannelInternal in;
740 SilcHashTableList htl;
742 SilcClientEntry entry;
743 unsigned char **res_argv = NULL;
744 SilcUInt32 *res_argv_lens = NULL, *res_argv_types = NULL, res_argc = 0;
746 SilcBool wait_res = FALSE;
748 assert(client && conn && channel);
750 SILC_LOG_DEBUG(("Start"));
752 in = silc_calloc(1, sizeof(*in));
755 in->channel_id = *channel->id;
756 in->completion = completion;
757 in->context = context;
759 /* If user list does not exist, send USERS command. */
760 if (!channel->user_list || !silc_hash_table_count(channel->user_list)) {
761 SILC_LOG_DEBUG(("Sending USERS"));
762 silc_client_command_register(client, SILC_COMMAND_USERS, NULL, NULL,
763 silc_client_command_reply_users_i, 0,
765 silc_client_command_send(client, conn, SILC_COMMAND_USERS,
766 conn->cmd_ident, 1, 2, channel->channel_name,
767 strlen(channel->channel_name));
768 silc_client_command_pending(conn, SILC_COMMAND_USERS, conn->cmd_ident,
769 silc_client_command_get_clients_by_channel_cb,
774 silc_hash_table_list(channel->user_list, &htl);
775 while (silc_hash_table_get(&htl, NULL, (void *)&chu)) {
778 /* If the entry has incomplete info, then resolve it from the server. */
779 if (!entry->nickname || !entry->realname) {
780 if (entry->status & SILC_CLIENT_STATUS_RESOLVING) {
781 /* Attach to this resolving and wait until it finishes */
782 silc_client_command_pending(
783 conn, SILC_COMMAND_NONE,
784 entry->resolve_cmd_ident,
785 silc_client_command_get_clients_by_channel_cb,
791 entry->status |= SILC_CLIENT_STATUS_RESOLVING;
792 entry->resolve_cmd_ident = conn->cmd_ident + 1;
794 idp = silc_id_payload_encode(entry->id, SILC_ID_CLIENT);
796 /* No we don't have it, query it from the server. Assemble argument
797 table that will be sent for the WHOIS command later. */
798 res_argv = silc_realloc(res_argv, sizeof(*res_argv) *
800 res_argv_lens = silc_realloc(res_argv_lens, sizeof(*res_argv_lens) *
802 res_argv_types = silc_realloc(res_argv_types, sizeof(*res_argv_types) *
804 res_argv[res_argc] = silc_memdup(idp->data, idp->len);
805 res_argv_lens[res_argc] = idp->len;
806 res_argv_types[res_argc] = res_argc + 4;
809 silc_buffer_free(idp);
812 silc_hash_table_list_reset(&htl);
814 /* Query the client information from server if the list included clients
815 that we don't know about. */
819 /* Send the WHOIS command to server */
820 res_cmd = silc_command_payload_encode(SILC_COMMAND_WHOIS,
821 res_argc, res_argv, res_argv_lens,
822 res_argv_types, ++conn->cmd_ident);
823 silc_client_packet_send(client, conn->sock, SILC_PACKET_COMMAND,
824 NULL, 0, NULL, NULL, res_cmd->data, res_cmd->len,
827 /* Register our own command reply for this command */
828 silc_client_command_register(client, SILC_COMMAND_WHOIS, NULL, NULL,
829 silc_client_command_reply_whois_i, 0,
832 /* Process the applications request after reply has been received */
833 silc_client_command_pending(
834 conn, SILC_COMMAND_WHOIS, conn->cmd_ident,
835 silc_client_command_get_clients_by_channel_cb,
839 silc_buffer_free(res_cmd);
841 silc_free(res_argv_lens);
842 silc_free(res_argv_types);
849 /* We have the clients in cache, get them and call the completion */
850 silc_client_command_get_clients_by_channel_cb((void *)in, NULL);
853 /* Finds entry for client by the client's ID. Returns the entry or NULL
854 if the entry was not found. */
856 SilcClientEntry silc_client_get_client_by_id(SilcClient client,
857 SilcClientConnection conn,
858 SilcClientID *client_id)
860 SilcIDCacheEntry id_cache;
862 assert(client && conn);
866 SILC_LOG_DEBUG(("Finding client by ID (%s)",
867 silc_id_render(client_id, SILC_ID_CLIENT)));
869 /* Find ID from cache */
870 if (!silc_idcache_find_by_id_one_ext(conn->internal->client_cache,
871 (void *)client_id, NULL, NULL,
872 silc_hash_client_id_compare, NULL,
876 SILC_LOG_DEBUG(("Found"));
878 return (SilcClientEntry)id_cache->context;
883 SilcClientConnection conn;
884 SilcClientID *client_id;
885 SilcGetClientCallback completion;
887 } *GetClientByIDInternal;
889 SILC_CLIENT_CMD_FUNC(get_client_by_id_callback)
891 GetClientByIDInternal i = (GetClientByIDInternal)context;
892 SilcClientEntry entry;
895 entry = silc_client_get_client_by_id(i->client, i->conn, i->client_id);
898 i->completion(i->client, i->conn, &entry, 1, i->context);
901 i->completion(i->client, i->conn, NULL, 0, i->context);
904 silc_free(i->client_id);
908 /* Same as above but will always resolve the information from the server.
909 Use this only if you know that you don't have the entry and the only
910 thing you know about the client is its ID. */
912 void silc_client_get_client_by_id_resolve(SilcClient client,
913 SilcClientConnection conn,
914 SilcClientID *client_id,
915 SilcBuffer attributes,
916 SilcGetClientCallback completion,
920 GetClientByIDInternal i = silc_calloc(1, sizeof(*i));
922 assert(client && conn && client_id);
924 SILC_LOG_DEBUG(("Start"));
928 i->client_id = silc_id_dup(client_id, SILC_ID_CLIENT);
929 i->completion = completion;
930 i->context = context;
932 /* Register our own command reply for this command */
933 silc_client_command_register(client, SILC_COMMAND_WHOIS, NULL, NULL,
934 silc_client_command_reply_whois_i, 0,
937 /* Send the command */
938 idp = silc_id_payload_encode(client_id, SILC_ID_CLIENT);
939 silc_client_command_send(client, conn, SILC_COMMAND_WHOIS, conn->cmd_ident,
940 2, 3, attributes ? attributes->data : NULL,
941 attributes ? attributes->len : 0,
942 4, idp->data, idp->len);
943 silc_buffer_free(idp);
945 /* Add pending callback */
946 silc_client_command_pending(conn, SILC_COMMAND_WHOIS, conn->cmd_ident,
947 silc_client_command_get_client_by_id_callback,
952 /******************************************************************************
954 Client, Channel and Server entry manipulation
956 ******************************************************************************/
959 /* Creates new client entry and adds it to the ID cache. Returns pointer
963 silc_client_add_client(SilcClient client, SilcClientConnection conn,
964 char *nickname, char *username,
965 char *userinfo, SilcClientID *id, SilcUInt32 mode)
967 SilcClientEntry client_entry;
970 SILC_LOG_DEBUG(("Start"));
972 /* Save the client infos */
973 client_entry = silc_calloc(1, sizeof(*client_entry));
974 client_entry->id = id;
975 client_entry->valid = TRUE;
976 silc_parse_userfqdn(nickname, &nick, &client_entry->server);
977 silc_parse_userfqdn(username, &client_entry->username,
978 &client_entry->hostname);
980 client_entry->realname = strdup(userinfo);
981 client_entry->mode = mode;
983 client_entry->nickname = strdup(nick);
984 client_entry->channels = silc_hash_table_alloc(1, silc_hash_ptr, NULL, NULL,
985 NULL, NULL, NULL, TRUE);
987 /* Normalize nickname */
988 if (client_entry->nickname) {
990 nick = silc_identifier_check(client_entry->nickname,
991 strlen(client_entry->nickname),
992 SILC_STRING_UTF8, 128, NULL);
994 silc_free(client_entry->nickname);
995 silc_free(client_entry->username);
996 silc_free(client_entry->hostname);
997 silc_free(client_entry->server);
998 silc_hash_table_free(client_entry->channels);
999 silc_free(client_entry);
1004 /* Format the nickname */
1005 silc_client_nickname_format(client, conn, client_entry);
1007 /* Add client to cache, the non-formatted nickname is saved to cache */
1008 if (!silc_idcache_add(conn->internal->client_cache, nick, client_entry->id,
1009 (void *)client_entry, 0, NULL)) {
1011 silc_free(client_entry->nickname);
1012 silc_free(client_entry->username);
1013 silc_free(client_entry->hostname);
1014 silc_free(client_entry->server);
1015 silc_hash_table_free(client_entry->channels);
1016 silc_free(client_entry);
1020 return client_entry;
1023 /* Updates the `client_entry' with the new information sent as argument. */
1025 void silc_client_update_client(SilcClient client,
1026 SilcClientConnection conn,
1027 SilcClientEntry client_entry,
1028 const char *nickname,
1029 const char *username,
1030 const char *userinfo,
1035 SILC_LOG_DEBUG(("Start"));
1037 if ((!client_entry->username || !client_entry->hostname) && username) {
1038 silc_free(client_entry->username);
1039 silc_free(client_entry->hostname);
1040 client_entry->username = NULL;
1041 client_entry->hostname = NULL;
1042 silc_parse_userfqdn(username, &client_entry->username,
1043 &client_entry->hostname);
1045 if (!client_entry->realname && userinfo)
1046 client_entry->realname = strdup(userinfo);
1047 if (!client_entry->nickname && nickname) {
1048 silc_parse_userfqdn(nickname, &nick, &client_entry->server);
1049 client_entry->nickname = strdup(nick);
1051 /* Normalize nickname */
1053 nick = silc_identifier_check(client_entry->nickname,
1054 strlen(client_entry->nickname),
1055 SILC_STRING_UTF8, 128, NULL);
1059 /* Format nickname */
1060 silc_client_nickname_format(client, conn, client_entry);
1062 client_entry->mode = mode;
1065 /* Remove the old cache entry and create a new one */
1066 silc_idcache_del_by_context(conn->internal->client_cache, client_entry);
1067 silc_idcache_add(conn->internal->client_cache, nick, client_entry->id,
1068 client_entry, 0, NULL);
1072 /* Deletes the client entry and frees all memory. */
1074 void silc_client_del_client_entry(SilcClient client,
1075 SilcClientConnection conn,
1076 SilcClientEntry client_entry)
1078 SILC_LOG_DEBUG(("Start"));
1080 silc_free(client_entry->nickname);
1081 silc_free(client_entry->username);
1082 silc_free(client_entry->realname);
1083 silc_free(client_entry->hostname);
1084 silc_free(client_entry->server);
1085 silc_free(client_entry->id);
1086 silc_free(client_entry->fingerprint);
1087 if (client_entry->public_key)
1088 silc_pkcs_public_key_free(client_entry->public_key);
1089 silc_hash_table_free(client_entry->channels);
1090 if (client_entry->send_key)
1091 silc_cipher_free(client_entry->send_key);
1092 if (client_entry->receive_key)
1093 silc_cipher_free(client_entry->receive_key);
1094 silc_free(client_entry->key);
1095 silc_client_ftp_session_free_client(conn, client_entry);
1096 if (client_entry->ke)
1097 silc_client_abort_key_agreement(client, conn, client_entry);
1098 silc_free(client_entry);
1101 /* Removes client from the cache by the client entry. */
1103 SilcBool silc_client_del_client(SilcClient client, SilcClientConnection conn,
1104 SilcClientEntry client_entry)
1106 SilcBool ret = silc_idcache_del_by_context(conn->internal->client_cache,
1110 /* Remove from channels */
1111 silc_client_remove_from_channels(client, conn, client_entry);
1113 /* Free the client entry data */
1114 silc_client_del_client_entry(client, conn, client_entry);
1120 /* Add new channel entry to the ID Cache */
1122 SilcChannelEntry silc_client_add_channel(SilcClient client,
1123 SilcClientConnection conn,
1124 const char *channel_name,
1126 SilcChannelID *channel_id)
1128 SilcChannelEntry channel;
1129 char *channel_namec;
1131 SILC_LOG_DEBUG(("Start"));
1133 channel = silc_calloc(1, sizeof(*channel));
1134 channel->channel_name = strdup(channel_name);
1135 channel->id = channel_id;
1136 channel->mode = mode;
1137 channel->user_list = silc_hash_table_alloc(1, silc_hash_ptr, NULL, NULL,
1138 NULL, NULL, NULL, TRUE);
1140 /* Normalize channel name */
1141 channel_namec = silc_channel_name_check(channel_name, strlen(channel_name),
1142 SILC_STRING_UTF8, 256, NULL);
1143 if (!channel_namec) {
1144 silc_free(channel->channel_name);
1145 silc_hash_table_free(channel->user_list);
1150 /* Put it to the ID cache */
1151 if (!silc_idcache_add(conn->internal->channel_cache, channel_namec,
1152 (void *)channel->id, (void *)channel, 0, NULL)) {
1153 silc_free(channel_namec);
1154 silc_free(channel->channel_name);
1155 silc_hash_table_free(channel->user_list);
1163 /* Foreach callbcak to free all users from the channel when deleting a
1166 static void silc_client_del_channel_foreach(void *key, void *context,
1169 SilcChannelUser chu = (SilcChannelUser)context;
1171 SILC_LOG_DEBUG(("Start"));
1173 /* Remove the context from the client's channel hash table as that
1174 table and channel's user_list hash table share this same context. */
1175 silc_hash_table_del(chu->client->channels, chu->channel);
1179 /* Removes channel from the cache by the channel entry. */
1181 SilcBool silc_client_del_channel(SilcClient client, SilcClientConnection conn,
1182 SilcChannelEntry channel)
1184 SilcBool ret = silc_idcache_del_by_context(conn->internal->channel_cache,
1187 SILC_LOG_DEBUG(("Start"));
1189 /* Free all client entrys from the users list. The silc_hash_table_free
1190 will free all the entries so they are not freed at the foreach
1192 silc_hash_table_foreach(channel->user_list, silc_client_del_channel_foreach,
1194 silc_hash_table_free(channel->user_list);
1196 silc_free(channel->channel_name);
1197 silc_free(channel->topic);
1198 silc_free(channel->id);
1199 if (channel->founder_key)
1200 silc_pkcs_public_key_free(channel->founder_key);
1201 silc_free(channel->key);
1202 if (channel->channel_key)
1203 silc_cipher_free(channel->channel_key);
1205 silc_hmac_free(channel->hmac);
1206 if (channel->old_channel_keys) {
1208 silc_dlist_start(channel->old_channel_keys);
1209 while ((key = silc_dlist_get(channel->old_channel_keys)) != SILC_LIST_END)
1210 silc_cipher_free(key);
1211 silc_dlist_uninit(channel->old_channel_keys);
1213 if (channel->old_hmacs) {
1215 silc_dlist_start(channel->old_hmacs);
1216 while ((hmac = silc_dlist_get(channel->old_hmacs)) != SILC_LIST_END)
1217 silc_hmac_free(hmac);
1218 silc_dlist_uninit(channel->old_hmacs);
1220 silc_schedule_task_del_by_context(conn->client->schedule, channel);
1221 silc_client_del_channel_private_keys(client, conn, channel);
1226 /* Replaces the channel ID of the `channel' to `new_id'. Returns FALSE
1227 if the ID could not be changed. */
1229 SilcBool silc_client_replace_channel_id(SilcClient client,
1230 SilcClientConnection conn,
1231 SilcChannelEntry channel,
1232 SilcChannelID *new_id)
1234 char *channel_namec;
1239 SILC_LOG_DEBUG(("Old Channel ID id(%s)",
1240 silc_id_render(channel->id, SILC_ID_CHANNEL)));
1241 SILC_LOG_DEBUG(("New Channel ID id(%s)",
1242 silc_id_render(new_id, SILC_ID_CHANNEL)));
1244 silc_idcache_del_by_id(conn->internal->channel_cache, channel->id);
1245 silc_free(channel->id);
1246 channel->id = new_id;
1248 /* Normalize channel name */
1249 channel_namec = silc_channel_name_check(channel->channel_name,
1250 strlen(channel->channel_name),
1251 SILC_STRING_UTF8, 256, NULL);
1255 return silc_idcache_add(conn->internal->channel_cache, channel_namec,
1256 (void *)channel->id, (void *)channel, 0, NULL);
1260 /* Finds entry for channel by the channel name. Returns the entry or NULL
1261 if the entry was not found. It is found only if the client is joined
1264 SilcChannelEntry silc_client_get_channel(SilcClient client,
1265 SilcClientConnection conn,
1268 SilcIDCacheEntry id_cache;
1269 SilcChannelEntry entry;
1271 assert(client && conn);
1275 SILC_LOG_DEBUG(("Start"));
1277 /* Normalize name for search */
1278 channel = silc_channel_name_check(channel, strlen(channel), SILC_STRING_UTF8,
1283 if (!silc_idcache_find_by_name_one(conn->internal->channel_cache, channel,
1289 entry = (SilcChannelEntry)id_cache->context;
1291 SILC_LOG_DEBUG(("Found"));
1298 /* Finds entry for channel by the channel ID. Returns the entry or NULL
1299 if the entry was not found. It is found only if the client is joined
1302 SilcChannelEntry silc_client_get_channel_by_id(SilcClient client,
1303 SilcClientConnection conn,
1304 SilcChannelID *channel_id)
1306 SilcIDCacheEntry id_cache;
1307 SilcChannelEntry entry;
1309 assert(client && conn);
1313 SILC_LOG_DEBUG(("Start"));
1315 if (!silc_idcache_find_by_id_one(conn->internal->channel_cache, channel_id,
1319 entry = (SilcChannelEntry)id_cache->context;
1321 SILC_LOG_DEBUG(("Found"));
1328 SilcClientConnection conn;
1330 SilcChannelID *channel_id;
1333 SilcGetChannelCallback completion;
1335 } *GetChannelInternal;
1337 SILC_CLIENT_CMD_FUNC(get_channel_resolve_callback)
1339 GetChannelInternal i = (GetChannelInternal)context;
1340 SilcChannelEntry entry;
1342 SILC_LOG_DEBUG(("Start"));
1344 /* Get the channel */
1345 entry = silc_client_get_channel(i->client, i->conn, i->u.channel_name);
1347 i->completion(i->client, i->conn, &entry, 1, i->context);
1349 i->completion(i->client, i->conn, NULL, 0, i->context);
1352 silc_free(i->u.channel_name);
1356 /* Resolves channel entry from the server by the channel name. */
1358 void silc_client_get_channel_resolve(SilcClient client,
1359 SilcClientConnection conn,
1361 SilcGetChannelCallback completion,
1364 GetChannelInternal i = silc_calloc(1, sizeof(*i));
1366 assert(client && conn && channel_name);
1368 SILC_LOG_DEBUG(("Start"));
1372 i->u.channel_name = strdup(channel_name);
1373 i->completion = completion;
1374 i->context = context;
1376 /* Register our own command reply for this command */
1377 silc_client_command_register(client, SILC_COMMAND_IDENTIFY, NULL, NULL,
1378 silc_client_command_reply_identify_i, 0,
1381 /* Send the command */
1382 silc_client_command_send(client, conn, SILC_COMMAND_IDENTIFY,
1384 1, 3, channel_name, strlen(channel_name));
1386 /* Add pending callback */
1387 silc_client_command_pending(conn, SILC_COMMAND_IDENTIFY, conn->cmd_ident,
1388 silc_client_command_get_channel_resolve_callback,
1392 SILC_CLIENT_CMD_FUNC(get_channel_by_id_callback)
1394 GetChannelInternal i = (GetChannelInternal)context;
1395 SilcChannelEntry entry;
1397 SILC_LOG_DEBUG(("Start"));
1399 /* Get the channel */
1400 entry = silc_client_get_channel_by_id(i->client, i->conn, i->u.channel_id);
1402 i->completion(i->client, i->conn, &entry, 1, i->context);
1404 i->completion(i->client, i->conn, NULL, 0, i->context);
1407 silc_free(i->u.channel_id);
1411 /* Resolves channel information from the server by the channel ID. */
1413 void silc_client_get_channel_by_id_resolve(SilcClient client,
1414 SilcClientConnection conn,
1415 SilcChannelID *channel_id,
1416 SilcGetChannelCallback completion,
1420 GetChannelInternal i = silc_calloc(1, sizeof(*i));
1422 assert(client && conn && channel_id);
1424 SILC_LOG_DEBUG(("Start"));
1428 i->u.channel_id = silc_id_dup(channel_id, SILC_ID_CHANNEL);
1429 i->completion = completion;
1430 i->context = context;
1432 /* Register our own command reply for this command */
1433 silc_client_command_register(client, SILC_COMMAND_IDENTIFY, NULL, NULL,
1434 silc_client_command_reply_identify_i, 0,
1437 /* Send the command */
1438 idp = silc_id_payload_encode(channel_id, SILC_ID_CHANNEL);
1439 silc_client_command_send(client, conn, SILC_COMMAND_IDENTIFY,
1441 1, 5, idp->data, idp->len);
1442 silc_buffer_free(idp);
1444 /* Add pending callback */
1445 silc_client_command_pending(conn, SILC_COMMAND_IDENTIFY, conn->cmd_ident,
1446 silc_client_command_get_channel_by_id_callback,
1450 /* Finds entry for server by the server name. */
1452 SilcServerEntry silc_client_get_server(SilcClient client,
1453 SilcClientConnection conn,
1456 SilcIDCacheEntry id_cache;
1457 SilcServerEntry entry;
1459 assert(client && conn);
1463 SILC_LOG_DEBUG(("Start"));
1465 /* Normalize server name for search */
1466 server_name = silc_identifier_check(server_name, strlen(server_name),
1467 SILC_STRING_UTF8, 256, NULL);
1471 if (!silc_idcache_find_by_name_one(conn->internal->server_cache,
1472 server_name, &id_cache)) {
1473 silc_free(server_name);
1477 entry = (SilcServerEntry)id_cache->context;
1479 silc_free(server_name);
1484 /* Finds entry for server by the server ID. */
1486 SilcServerEntry silc_client_get_server_by_id(SilcClient client,
1487 SilcClientConnection conn,
1488 SilcServerID *server_id)
1490 SilcIDCacheEntry id_cache;
1491 SilcServerEntry entry;
1493 assert(client && conn);
1497 SILC_LOG_DEBUG(("Start"));
1499 if (!silc_idcache_find_by_id_one(conn->internal->server_cache,
1500 (void *)server_id, &id_cache))
1503 entry = (SilcServerEntry)id_cache->context;
1508 /* Add new server entry */
1510 SilcServerEntry silc_client_add_server(SilcClient client,
1511 SilcClientConnection conn,
1512 const char *server_name,
1513 const char *server_info,
1514 SilcServerID *server_id)
1516 SilcServerEntry server_entry;
1517 char *server_namec = NULL;
1519 SILC_LOG_DEBUG(("Start"));
1521 server_entry = silc_calloc(1, sizeof(*server_entry));
1522 if (!server_entry || !server_id)
1525 server_entry->server_id = server_id;
1527 server_entry->server_name = strdup(server_name);
1529 server_entry->server_info = strdup(server_info);
1531 /* Normalize server name */
1533 server_namec = silc_identifier_check(server_name, strlen(server_name),
1534 SILC_STRING_UTF8, 256, NULL);
1535 if (!server_namec) {
1536 silc_free(server_entry->server_id);
1537 silc_free(server_entry->server_name);
1538 silc_free(server_entry->server_info);
1539 silc_free(server_entry);
1544 /* Add server to cache */
1545 if (!silc_idcache_add(conn->internal->server_cache, server_namec,
1546 server_entry->server_id, server_entry, 0, NULL)) {
1547 silc_free(server_namec);
1548 silc_free(server_entry->server_id);
1549 silc_free(server_entry->server_name);
1550 silc_free(server_entry->server_info);
1551 silc_free(server_entry);
1555 return server_entry;
1558 /* Removes server from the cache by the server entry. */
1560 SilcBool silc_client_del_server(SilcClient client, SilcClientConnection conn,
1561 SilcServerEntry server)
1563 SilcBool ret = silc_idcache_del_by_context(conn->internal->server_cache, server);
1564 silc_free(server->server_name);
1565 silc_free(server->server_info);
1566 silc_free(server->server_id);
1571 /* Updates the `server_entry' with the new information sent as argument. */
1573 void silc_client_update_server(SilcClient client,
1574 SilcClientConnection conn,
1575 SilcServerEntry server_entry,
1576 const char *server_name,
1577 const char *server_info)
1579 char *server_namec = NULL;
1581 SILC_LOG_DEBUG(("Start"));
1584 (!server_entry->server_name ||
1585 !silc_utf8_strcasecmp(server_entry->server_name, server_name))) {
1587 silc_idcache_del_by_context(conn->internal->server_cache, server_entry);
1588 silc_free(server_entry->server_name);
1589 server_entry->server_name = strdup(server_name);
1591 /* Normalize server name */
1593 server_namec = silc_identifier_check(server_name, strlen(server_name),
1594 SILC_STRING_UTF8, 256, NULL);
1598 silc_idcache_add(conn->internal->server_cache, server_namec,
1599 server_entry->server_id,
1600 server_entry, 0, NULL);
1605 (!server_entry->server_info ||
1606 !silc_utf8_strcasecmp(server_entry->server_info, server_info))) {
1607 silc_free(server_entry->server_info);
1608 server_entry->server_info = strdup(server_info);
1612 /* Formats the nickname of the client specified by the `client_entry'.
1613 If the format is specified by the application this will format the
1614 nickname and replace the old nickname in the client entry. If the
1615 format string is not specified then this function has no effect. */
1617 void silc_client_nickname_format(SilcClient client,
1618 SilcClientConnection conn,
1619 SilcClientEntry client_entry)
1622 char *newnick = NULL;
1623 int i, off = 0, len;
1625 SilcClientEntry *clients;
1626 SilcUInt32 clients_count = 0;
1627 SilcClientEntry unformatted = NULL;
1629 SILC_LOG_DEBUG(("Start"));
1631 if (!client->internal->params->nickname_format[0])
1634 if (!client_entry->nickname)
1637 /* Get all clients with same nickname. Do not perform the formatting
1638 if there aren't any clients with same nickname unless the application
1639 is forcing us to do so. */
1640 clients = silc_client_get_clients_local(client, conn,
1641 client_entry->nickname, NULL,
1643 if (!clients && !client->internal->params->nickname_force_format)
1648 for (i = 0; i < clients_count; i++) {
1649 if (clients[i]->valid && clients[i] != client_entry)
1651 if (clients[i]->valid && clients[i] != client_entry &&
1652 silc_utf8_strcasecmp(clients[i]->nickname, client_entry->nickname))
1655 if (!len || freebase)
1658 if (clients_count == 1)
1659 unformatted = clients[0];
1661 for (i = 0; i < clients_count; i++)
1662 if (silc_utf8_strncasecmp(clients[i]->nickname, client_entry->nickname,
1663 strlen(clients[i]->nickname)))
1664 unformatted = clients[i];
1666 /* If we are changing nickname of our local entry we'll enforce
1667 that we will always get the unformatted nickname. Give our
1668 format number to the one that is not formatted now. */
1669 if (unformatted && client_entry == conn->local_entry)
1670 client_entry = unformatted;
1672 cp = client->internal->params->nickname_format;
1682 if (!client_entry->nickname)
1684 len = strlen(client_entry->nickname);
1685 newnick = silc_realloc(newnick, sizeof(*newnick) * (off + len));
1686 memcpy(&newnick[off], client_entry->nickname, len);
1690 /* Stripped hostname */
1691 if (!client_entry->hostname)
1693 len = strcspn(client_entry->hostname, ".");
1694 i = strcspn(client_entry->hostname, "-");
1697 newnick = silc_realloc(newnick, sizeof(*newnick) * (off + len));
1698 memcpy(&newnick[off], client_entry->hostname, len);
1703 if (!client_entry->hostname)
1705 len = strlen(client_entry->hostname);
1706 newnick = silc_realloc(newnick, sizeof(*newnick) * (off + len));
1707 memcpy(&newnick[off], client_entry->hostname, len);
1711 /* Stripped server name */
1712 if (!client_entry->server)
1714 len = strcspn(client_entry->server, ".");
1715 newnick = silc_realloc(newnick, sizeof(*newnick) * (off + len));
1716 memcpy(&newnick[off], client_entry->server, len);
1720 /* Full server name */
1721 if (!client_entry->server)
1723 len = strlen(client_entry->server);
1724 newnick = silc_realloc(newnick, sizeof(*newnick) * (off + len));
1725 memcpy(&newnick[off], client_entry->server, len);
1729 /* Ascending number */
1734 if (clients_count == 1)
1737 for (i = 0; i < clients_count; i++) {
1738 if (!silc_utf8_strncasecmp(clients[i]->nickname, newnick, off))
1740 if (strlen(clients[i]->nickname) <= off)
1742 num = atoi(&clients[i]->nickname[off]);
1747 memset(tmp, 0, sizeof(tmp));
1748 snprintf(tmp, sizeof(tmp) - 1, "%d", ++max);
1750 newnick = silc_realloc(newnick, sizeof(*newnick) * (off + len));
1751 memcpy(&newnick[off], tmp, len);
1756 /* Some other character in the string */
1757 newnick = silc_realloc(newnick, sizeof(*newnick) * (off + 1));
1758 memcpy(&newnick[off], cp, 1);
1766 newnick = silc_realloc(newnick, sizeof(*newnick) * (off + 1));
1769 silc_free(client_entry->nickname);
1770 client_entry->nickname = newnick;