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;
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 (!silc_utf8_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;
124 SilcClientEntry *clients;
125 SilcUInt32 clients_count;
126 } *GetClientInternal;
128 /* Completion for IDENTIFY */
130 SILC_CLIENT_CMD_FUNC(get_client_callback)
132 GetClientInternal i = (GetClientInternal)context;
133 SilcClientEntry *clients;
134 SilcUInt32 clients_count;
136 /* Get the clients */
137 clients = silc_client_get_clients_local(i->client, i->conn,
141 i->completion(i->client, i->conn, clients, clients_count, i->context);
144 i->completion(i->client, i->conn, NULL, 0, i->context);
147 silc_free(i->nickname);
151 /* Completion for WHOIS */
153 SILC_CLIENT_CMD_FUNC(get_client_callback_wc)
155 GetClientInternal i = (GetClientInternal)context;
156 SilcClientCommandReplyContext cmd = context2;
157 SilcClientID *client_id = NULL;
158 SilcClientEntry client_entry = NULL;
159 unsigned char *id_data;
162 /* Get the client entry just returned from server */
163 id_data = silc_argument_get_arg_type(cmd->args, 2, &len);
165 client_id = silc_id_payload_parse_id(id_data, len, NULL);
167 client_entry = silc_client_get_client_by_id(i->client,
170 if (!SILC_STATUS_IS_ERROR(cmd->status) &&
171 cmd->status != SILC_STATUS_OK &&
172 cmd->status != SILC_STATUS_LIST_END) {
173 silc_free(client_id);
177 i->completion(i->client, i->conn, i->clients, i->clients_count,
179 silc_free(client_id);
180 silc_free(i->clients);
181 silc_free(i->nickname);
186 /* Save the client */
187 i->clients = silc_realloc(i->clients,
188 (sizeof(*i->clients) * (i->clients_count + 1)));
189 i->clients[i->clients_count] = client_entry;
192 /* Return if more data is expected */
193 if (cmd->status != SILC_STATUS_OK &&
194 cmd->status != SILC_STATUS_LIST_END) {
195 silc_free(client_id);
199 i->completion(i->client, i->conn, i->clients, i->clients_count,
202 silc_free(client_id);
203 silc_free(i->clients);
204 silc_free(i->nickname);
208 /* Our own WHOIS reply processor. */
210 SILC_CLIENT_CMD_FUNC(get_client_callback_w)
212 SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
213 SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
215 SILC_LOG_DEBUG(("Start"));
217 if (!silc_command_get_status(cmd->payload, NULL, NULL)) {
218 if (SILC_STATUS_IS_ERROR(cmd->status))
220 if (cmd->status == SILC_STATUS_LIST_END)
225 /* Save WHOIS info */
226 silc_client_command_reply_whois_save(cmd, cmd->status, FALSE);
228 /* Call pending completion for each reply */
229 if (cmd->status != SILC_STATUS_OK &&
230 cmd->status != SILC_STATUS_LIST_END) {
231 if (cmd->callbacks[0].callback)
232 (*cmd->callbacks[0].callback)(cmd->callbacks[0].context, cmd);
233 silc_client_command_reply_free(cmd);
238 SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_WHOIS);
241 /* If we received notify for invalid ID we'll remove the ID if we
243 if (cmd->error == SILC_STATUS_ERR_NO_SUCH_CLIENT_ID) {
244 SilcClientEntry client_entry;
247 silc_argument_get_arg_type(silc_command_get_args(cmd->payload),
250 SilcClientID *client_id = silc_id_payload_parse_id(tmp, tmp_len, NULL);
252 client_entry = silc_client_get_client_by_id(cmd->client, conn,
255 silc_client_del_client(cmd->client, conn, client_entry);
256 silc_free(client_id);
261 /* Unregister this command reply */
262 silc_client_command_unregister(cmd->client, SILC_COMMAND_WHOIS,
263 NULL, silc_client_command_reply_whois_i,
265 silc_client_command_reply_free(cmd);
268 /* Finds client entry or entries by the `nickname' and `server'. The
269 completion callback will be called when the client entries has been found.
271 Note: this function is always asynchronous and resolves the client
272 information from the server. Thus, if you already know the client
273 information then use the silc_client_get_client_by_id function to
274 get the client entry since this function may be very slow and should
275 be used only to initially get the client entries. */
277 void silc_client_get_clients_i(SilcClient client,
278 SilcClientConnection conn,
280 const char *nickname,
282 SilcBuffer attributes,
283 SilcGetClientCallback completion,
288 char *userhost = NULL;
290 assert(client && conn);
292 if (!nickname && !attributes)
295 i = silc_calloc(1, sizeof(*i));
298 i->nickname = nickname ? strdup(nickname) : NULL;
299 i->completion = completion;
300 i->context = context;
302 if (nickname && server) {
303 len = strlen(nickname) + strlen(server) + 3;
304 userhost = silc_calloc(len, sizeof(*userhost));
305 silc_strncat(userhost, len, nickname, strlen(nickname));
306 silc_strncat(userhost, len, "@", 1);
307 silc_strncat(userhost, len, server, strlen(server));
308 } else if (nickname) {
309 userhost = silc_memdup(nickname, strlen(nickname));
312 /* Register our own command reply for this command */
313 if (command == SILC_COMMAND_IDENTIFY) {
314 silc_client_command_register(client, command, NULL, NULL,
315 silc_client_command_reply_identify_i, 0,
317 /* Send the command */
318 silc_client_command_send(client, conn, SILC_COMMAND_IDENTIFY,
319 conn->cmd_ident, 1, 1, userhost,
322 /* Add pending callback */
323 silc_client_command_pending(conn, command, conn->cmd_ident,
324 silc_client_command_get_client_callback,
327 silc_client_command_register(client, command, NULL, NULL,
328 silc_client_command_get_client_callback_w, 0,
330 /* Send the command */
331 silc_client_command_send(client, conn, command, conn->cmd_ident, 2,
332 1, userhost, userhost ? strlen(userhost) : 0,
333 3, attributes ? attributes->data : NULL,
334 attributes ? attributes->len : 0);
336 /* Add pending callback */
337 silc_client_command_pending(conn, command, conn->cmd_ident,
338 silc_client_command_get_client_callback_wc,
344 void silc_client_get_clients(SilcClient client,
345 SilcClientConnection conn,
346 const char *nickname,
348 SilcGetClientCallback completion,
351 silc_client_get_clients_i(client, conn, SILC_COMMAND_IDENTIFY,
352 nickname, server, NULL,
353 completion, context);
356 void silc_client_get_clients_whois(SilcClient client,
357 SilcClientConnection conn,
358 const char *nickname,
360 SilcBuffer attributes,
361 SilcGetClientCallback completion,
364 silc_client_get_clients_i(client, conn, SILC_COMMAND_WHOIS,
365 nickname, server, attributes,
366 completion, context);
369 /* The old style function to find client entry. This is used by the
370 library internally. If `query' is TRUE then the client information is
371 requested by the server. The pending command callback must be set
373 /* XXX This function should be removed */
375 SilcClientEntry silc_idlist_get_client(SilcClient client,
376 SilcClientConnection conn,
377 const char *nickname,
381 SilcIDCacheEntry id_cache;
382 SilcIDCacheList list = NULL;
383 SilcClientEntry entry = NULL;
385 SILC_LOG_DEBUG(("Start"));
387 /* Find ID from cache */
388 if (!silc_idcache_find_by_name(conn->internal->client_cache,
389 (char *)nickname, &list)) {
393 SILC_LOG_DEBUG(("Requesting Client ID from server"));
395 /* Register our own command reply for this command */
396 silc_client_command_register(client, SILC_COMMAND_IDENTIFY, NULL, NULL,
397 silc_client_command_reply_identify_i, 0,
400 /* Send the command */
401 silc_client_command_send(client, conn, SILC_COMMAND_IDENTIFY,
402 conn->cmd_ident, 1, 1, nickname,
406 silc_idcache_list_free(list);
414 /* Take first found cache entry */
415 if (!silc_idcache_list_first(list, &id_cache))
418 entry = (SilcClientEntry)id_cache->context;
420 /* Check multiple cache entries for match */
421 silc_idcache_list_first(list, &id_cache);
423 entry = (SilcClientEntry)id_cache->context;
425 if (!silc_utf8_strcasecmp(entry->nickname, format)) {
426 if (!silc_idcache_list_next(list, &id_cache)) {
438 /* If match weren't found, request it */
444 silc_idcache_list_free(list);
451 SilcClientConnection conn;
452 SilcUInt32 list_count;
453 SilcBuffer client_id_list;
454 SilcGetClientCallback completion;
457 } *GetClientsByListInternal;
459 SILC_CLIENT_CMD_FUNC(get_clients_list_callback)
461 GetClientsByListInternal i = (GetClientsByListInternal)context;
462 SilcIDCacheEntry id_cache = NULL;
463 SilcBuffer client_id_list = i->client_id_list;
464 SilcClientEntry *clients = NULL;
465 SilcUInt32 clients_count = 0;
469 SILC_LOG_DEBUG(("Start"));
477 SILC_LOG_DEBUG(("Resolved all clients"));
479 clients = silc_calloc(i->list_count, sizeof(*clients));
481 for (c = 0; c < i->list_count; c++) {
483 SilcClientID *client_id;
486 SILC_GET16_MSB(idp_len, client_id_list->data + 2);
488 client_id = silc_id_payload_parse_id(client_id_list->data, idp_len, NULL);
490 silc_buffer_pull(client_id_list, idp_len);
494 /* Get the client entry */
495 if (silc_idcache_find_by_id_one_ext(i->conn->internal->client_cache,
498 silc_hash_client_id_compare, NULL,
500 clients[clients_count] = (SilcClientEntry)id_cache->context;
505 silc_free(client_id);
506 silc_buffer_pull(client_id_list, idp_len);
510 i->completion(i->client, i->conn, clients, clients_count, i->context);
513 i->completion(i->client, i->conn, NULL, 0, i->context);
516 if (i->client_id_list)
517 silc_buffer_free(i->client_id_list);
521 /* Gets client entries by the list of client ID's `client_id_list'. This
522 always resolves those client ID's it does not know yet from the server
523 so this function might take a while. The `client_id_list' is a list
524 of ID Payloads added one after other. JOIN command reply and USERS
525 command reply for example returns this sort of list. The `completion'
526 will be called after the entries are available. */
528 void silc_client_get_clients_by_list(SilcClient client,
529 SilcClientConnection conn,
530 SilcUInt32 list_count,
531 SilcBuffer client_id_list,
532 SilcGetClientCallback completion,
535 SilcIDCacheEntry id_cache = NULL;
537 unsigned char **res_argv = NULL;
538 SilcUInt32 *res_argv_lens = NULL, *res_argv_types = NULL, res_argc = 0;
539 GetClientsByListInternal in;
540 bool wait_res = FALSE;
542 assert(client && conn && client_id_list);
544 SILC_LOG_DEBUG(("Start"));
546 in = silc_calloc(1, sizeof(*in));
549 in->list_count = list_count;
550 in->client_id_list = silc_buffer_copy(client_id_list);
551 in->completion = completion;
552 in->context = context;
554 for (i = 0; i < list_count; i++) {
556 SilcClientID *client_id;
557 SilcClientEntry entry;
561 SILC_GET16_MSB(idp_len, client_id_list->data + 2);
563 client_id = silc_id_payload_parse_id(client_id_list->data, idp_len, NULL);
565 silc_buffer_pull(client_id_list, idp_len);
569 /* Check if we have this client cached already. */
571 silc_idcache_find_by_id_one_ext(conn->internal->client_cache,
572 (void *)client_id, NULL, NULL,
573 silc_hash_client_id_compare, NULL,
576 /* If we don't have the entry or it has incomplete info, then resolve
577 it from the server. */
578 if (!ret || !((SilcClientEntry)id_cache->context)->nickname) {
579 entry = ret ? (SilcClientEntry)id_cache->context : NULL;
582 if (entry->status & SILC_CLIENT_STATUS_RESOLVING) {
583 /* Attach to this resolving and wait until it finishes */
584 silc_client_command_pending(
585 conn, SILC_COMMAND_NONE,
586 entry->resolve_cmd_ident,
587 silc_client_command_get_clients_list_callback,
592 silc_free(client_id);
593 silc_buffer_pull(client_id_list, idp_len);
597 entry->status |= SILC_CLIENT_STATUS_RESOLVING;
598 entry->resolve_cmd_ident = conn->cmd_ident + 1;
601 /* No we don't have it, query it from the server. Assemble argument
602 table that will be sent for the IDENTIFY command later. */
603 res_argv = silc_realloc(res_argv, sizeof(*res_argv) *
605 res_argv_lens = silc_realloc(res_argv_lens, sizeof(*res_argv_lens) *
607 res_argv_types = silc_realloc(res_argv_types, sizeof(*res_argv_types) *
609 res_argv[res_argc] = client_id_list->data;
610 res_argv_lens[res_argc] = idp_len;
611 res_argv_types[res_argc] = res_argc + 5;
615 silc_free(client_id);
616 silc_buffer_pull(client_id_list, idp_len);
619 silc_buffer_push(client_id_list, client_id_list->data -
620 client_id_list->head);
622 /* Query the client information from server if the list included clients
623 that we don't know about. */
627 /* Send the IDENTIFY command to server */
628 res_cmd = silc_command_payload_encode(SILC_COMMAND_IDENTIFY,
629 res_argc, res_argv, res_argv_lens,
630 res_argv_types, ++conn->cmd_ident);
631 silc_client_packet_send(client, conn->sock, SILC_PACKET_COMMAND,
632 NULL, 0, NULL, NULL, res_cmd->data, res_cmd->len,
635 /* Register our own command reply for this command */
636 silc_client_command_register(client, SILC_COMMAND_IDENTIFY, NULL, NULL,
637 silc_client_command_reply_identify_i, 0,
640 /* Process the applications request after reply has been received */
641 silc_client_command_pending(conn, SILC_COMMAND_IDENTIFY, conn->cmd_ident,
642 silc_client_command_get_clients_list_callback,
646 silc_buffer_free(res_cmd);
648 silc_free(res_argv_lens);
649 silc_free(res_argv_types);
656 /* We have the clients in cache, get them and call the completion */
657 silc_client_command_get_clients_list_callback((void *)in, NULL);
662 SilcClientConnection conn;
663 SilcChannelID channel_id;
664 SilcGetClientCallback completion;
667 } *GetClientsByChannelInternal;
669 SILC_CLIENT_CMD_FUNC(get_clients_by_channel_cb)
671 GetClientsByChannelInternal i = context;
672 SilcClientEntry *clients = NULL;
673 SilcUInt32 clients_count = 0;
675 SilcChannelEntry channel;
676 SilcHashTableList htl;
679 channel = silc_client_get_channel_by_id(i->client, i->conn, &i->channel_id);
680 if (channel && !silc_hash_table_count(channel->user_list)) {
681 clients = silc_calloc(silc_hash_table_count(channel->user_list),
683 silc_hash_table_list(channel->user_list, &htl);
684 while (silc_hash_table_get(&htl, NULL, (void *)&chu))
685 clients[clients_count++] = chu->client;
686 silc_hash_table_list_reset(&htl);
691 i->completion(i->client, i->conn, clients, clients_count, i->context);
694 i->completion(i->client, i->conn, NULL, 0, i->context);
700 /* Gets client entries by the channel entry indicated by `channel'. Thus,
701 it resolves the clients currently on that channel. */
703 void silc_client_get_clients_by_channel(SilcClient client,
704 SilcClientConnection conn,
705 SilcChannelEntry channel,
706 SilcGetClientCallback completion,
709 GetClientsByChannelInternal in;
710 SilcHashTableList htl;
712 SilcClientEntry entry;
713 unsigned char **res_argv = NULL;
714 SilcUInt32 *res_argv_lens = NULL, *res_argv_types = NULL, res_argc = 0;
716 bool wait_res = FALSE;
718 assert(client && conn && channel);
720 SILC_LOG_DEBUG(("Start"));
722 in = silc_calloc(1, sizeof(*in));
725 in->channel_id = *channel->id;
726 in->completion = completion;
727 in->context = context;
729 /* If user list does not exist, send USERS command. */
730 if (!channel->user_list || !silc_hash_table_count(channel->user_list)) {
731 SILC_LOG_DEBUG(("Sending USERS"));
732 silc_client_command_register(client, SILC_COMMAND_USERS, NULL, NULL,
733 silc_client_command_reply_users_i, 0,
735 silc_client_command_send(client, conn, SILC_COMMAND_USERS,
736 conn->cmd_ident, 1, 2, channel->channel_name,
737 strlen(channel->channel_name));
738 silc_client_command_pending(conn, SILC_COMMAND_USERS, conn->cmd_ident,
739 silc_client_command_get_clients_by_channel_cb,
744 silc_hash_table_list(channel->user_list, &htl);
745 while (silc_hash_table_get(&htl, NULL, (void *)&chu)) {
748 /* If the entry has incomplete info, then resolve it from the server. */
749 if (!entry->nickname || !entry->realname) {
750 if (entry->status & SILC_CLIENT_STATUS_RESOLVING) {
751 /* Attach to this resolving and wait until it finishes */
752 silc_client_command_pending(
753 conn, SILC_COMMAND_NONE,
754 entry->resolve_cmd_ident,
755 silc_client_command_get_clients_by_channel_cb,
761 entry->status |= SILC_CLIENT_STATUS_RESOLVING;
762 entry->resolve_cmd_ident = conn->cmd_ident + 1;
764 idp = silc_id_payload_encode(entry->id, SILC_ID_CLIENT);
766 /* No we don't have it, query it from the server. Assemble argument
767 table that will be sent for the WHOIS command later. */
768 res_argv = silc_realloc(res_argv, sizeof(*res_argv) *
770 res_argv_lens = silc_realloc(res_argv_lens, sizeof(*res_argv_lens) *
772 res_argv_types = silc_realloc(res_argv_types, sizeof(*res_argv_types) *
774 res_argv[res_argc] = silc_memdup(idp->data, idp->len);
775 res_argv_lens[res_argc] = idp->len;
776 res_argv_types[res_argc] = res_argc + 4;
779 silc_buffer_free(idp);
782 silc_hash_table_list_reset(&htl);
784 /* Query the client information from server if the list included clients
785 that we don't know about. */
789 /* Send the WHOIS command to server */
790 res_cmd = silc_command_payload_encode(SILC_COMMAND_WHOIS,
791 res_argc, res_argv, res_argv_lens,
792 res_argv_types, ++conn->cmd_ident);
793 silc_client_packet_send(client, conn->sock, SILC_PACKET_COMMAND,
794 NULL, 0, NULL, NULL, res_cmd->data, res_cmd->len,
797 /* Register our own command reply for this command */
798 silc_client_command_register(client, SILC_COMMAND_WHOIS, NULL, NULL,
799 silc_client_command_reply_whois_i, 0,
802 /* Process the applications request after reply has been received */
803 silc_client_command_pending(
804 conn, SILC_COMMAND_WHOIS, conn->cmd_ident,
805 silc_client_command_get_clients_by_channel_cb,
809 silc_buffer_free(res_cmd);
811 silc_free(res_argv_lens);
812 silc_free(res_argv_types);
819 /* We have the clients in cache, get them and call the completion */
820 silc_client_command_get_clients_by_channel_cb((void *)in, NULL);
823 /* Finds entry for client by the client's ID. Returns the entry or NULL
824 if the entry was not found. */
826 SilcClientEntry silc_client_get_client_by_id(SilcClient client,
827 SilcClientConnection conn,
828 SilcClientID *client_id)
830 SilcIDCacheEntry id_cache;
832 assert(client && conn);
836 SILC_LOG_DEBUG(("Finding client by ID (%s)",
837 silc_id_render(client_id, SILC_ID_CLIENT)));
839 /* Find ID from cache */
840 if (!silc_idcache_find_by_id_one_ext(conn->internal->client_cache,
841 (void *)client_id, NULL, NULL,
842 silc_hash_client_id_compare, NULL,
846 SILC_LOG_DEBUG(("Found"));
848 return (SilcClientEntry)id_cache->context;
853 SilcClientConnection conn;
854 SilcClientID *client_id;
855 SilcGetClientCallback completion;
857 } *GetClientByIDInternal;
859 SILC_CLIENT_CMD_FUNC(get_client_by_id_callback)
861 GetClientByIDInternal i = (GetClientByIDInternal)context;
862 SilcClientEntry entry;
865 entry = silc_client_get_client_by_id(i->client, i->conn, i->client_id);
868 i->completion(i->client, i->conn, &entry, 1, i->context);
871 i->completion(i->client, i->conn, NULL, 0, i->context);
874 silc_free(i->client_id);
878 /* Same as above but will always resolve the information from the server.
879 Use this only if you know that you don't have the entry and the only
880 thing you know about the client is its ID. */
882 void silc_client_get_client_by_id_resolve(SilcClient client,
883 SilcClientConnection conn,
884 SilcClientID *client_id,
885 SilcBuffer attributes,
886 SilcGetClientCallback completion,
890 GetClientByIDInternal i = silc_calloc(1, sizeof(*i));
892 assert(client && conn && client_id);
894 SILC_LOG_DEBUG(("Start"));
898 i->client_id = silc_id_dup(client_id, SILC_ID_CLIENT);
899 i->completion = completion;
900 i->context = context;
902 /* Register our own command reply for this command */
903 silc_client_command_register(client, SILC_COMMAND_WHOIS, NULL, NULL,
904 silc_client_command_reply_whois_i, 0,
907 /* Send the command */
908 idp = silc_id_payload_encode(client_id, SILC_ID_CLIENT);
909 silc_client_command_send(client, conn, SILC_COMMAND_WHOIS, conn->cmd_ident,
910 2, 3, attributes ? attributes->data : NULL,
911 attributes ? attributes->len : 0,
912 4, idp->data, idp->len);
913 silc_buffer_free(idp);
915 /* Add pending callback */
916 silc_client_command_pending(conn, SILC_COMMAND_WHOIS, conn->cmd_ident,
917 silc_client_command_get_client_by_id_callback,
922 /******************************************************************************
924 Client, Channel and Server entry manipulation
926 ******************************************************************************/
929 /* Creates new client entry and adds it to the ID cache. Returns pointer
933 silc_client_add_client(SilcClient client, SilcClientConnection conn,
934 char *nickname, char *username,
935 char *userinfo, SilcClientID *id, SilcUInt32 mode)
937 SilcClientEntry client_entry;
940 SILC_LOG_DEBUG(("Start"));
942 /* Save the client infos */
943 client_entry = silc_calloc(1, sizeof(*client_entry));
944 client_entry->id = id;
945 client_entry->valid = TRUE;
946 silc_parse_userfqdn(nickname, &nick, &client_entry->server);
947 silc_parse_userfqdn(username, &client_entry->username,
948 &client_entry->hostname);
950 client_entry->realname = strdup(userinfo);
951 client_entry->mode = mode;
953 client_entry->nickname = strdup(nick);
954 client_entry->channels = silc_hash_table_alloc(1, silc_hash_ptr, NULL, NULL,
955 NULL, NULL, NULL, TRUE);
957 /* Format the nickname */
958 silc_client_nickname_format(client, conn, client_entry);
960 /* Add client to cache, the non-formatted nickname is saved to cache */
961 if (!silc_idcache_add(conn->internal->client_cache, nick, client_entry->id,
962 (void *)client_entry, 0, NULL)) {
963 silc_free(client_entry->nickname);
964 silc_free(client_entry->username);
965 silc_free(client_entry->hostname);
966 silc_free(client_entry->server);
967 silc_hash_table_free(client_entry->channels);
968 silc_free(client_entry);
975 /* Updates the `client_entry' with the new information sent as argument. */
977 void silc_client_update_client(SilcClient client,
978 SilcClientConnection conn,
979 SilcClientEntry client_entry,
980 const char *nickname,
981 const char *username,
982 const char *userinfo,
987 SILC_LOG_DEBUG(("Start"));
989 if ((!client_entry->username || !client_entry->hostname) && username) {
990 silc_free(client_entry->username);
991 silc_free(client_entry->hostname);
992 client_entry->username = NULL;
993 client_entry->hostname = NULL;
994 silc_parse_userfqdn(username, &client_entry->username,
995 &client_entry->hostname);
997 if (!client_entry->realname && userinfo)
998 client_entry->realname = strdup(userinfo);
999 if (!client_entry->nickname && nickname) {
1000 silc_parse_userfqdn(nickname, &nick, &client_entry->server);
1001 client_entry->nickname = strdup(nick);
1002 silc_client_nickname_format(client, conn, client_entry);
1004 client_entry->mode = mode;
1007 /* Remove the old cache entry and create a new one */
1008 silc_idcache_del_by_context(conn->internal->client_cache, client_entry);
1009 silc_idcache_add(conn->internal->client_cache, nick, client_entry->id,
1010 client_entry, 0, NULL);
1014 /* Deletes the client entry and frees all memory. */
1016 void silc_client_del_client_entry(SilcClient client,
1017 SilcClientConnection conn,
1018 SilcClientEntry client_entry)
1020 SILC_LOG_DEBUG(("Start"));
1022 silc_free(client_entry->nickname);
1023 silc_free(client_entry->username);
1024 silc_free(client_entry->realname);
1025 silc_free(client_entry->hostname);
1026 silc_free(client_entry->server);
1027 silc_free(client_entry->id);
1028 silc_free(client_entry->fingerprint);
1029 if (client_entry->public_key)
1030 silc_pkcs_public_key_free(client_entry->public_key);
1031 silc_hash_table_free(client_entry->channels);
1032 if (client_entry->send_key)
1033 silc_cipher_free(client_entry->send_key);
1034 if (client_entry->receive_key)
1035 silc_cipher_free(client_entry->receive_key);
1036 silc_free(client_entry->key);
1037 silc_client_ftp_session_free_client(conn, client_entry);
1038 if (client_entry->ke)
1039 silc_client_abort_key_agreement(client, conn, client_entry);
1040 silc_free(client_entry);
1043 /* Removes client from the cache by the client entry. */
1045 bool silc_client_del_client(SilcClient client, SilcClientConnection conn,
1046 SilcClientEntry client_entry)
1048 bool ret = silc_idcache_del_by_context(conn->internal->client_cache,
1052 /* Remove from channels */
1053 silc_client_remove_from_channels(client, conn, client_entry);
1055 /* Free the client entry data */
1056 silc_client_del_client_entry(client, conn, client_entry);
1062 /* Add new channel entry to the ID Cache */
1064 SilcChannelEntry silc_client_add_channel(SilcClient client,
1065 SilcClientConnection conn,
1066 const char *channel_name,
1068 SilcChannelID *channel_id)
1070 SilcChannelEntry channel;
1072 SILC_LOG_DEBUG(("Start"));
1074 channel = silc_calloc(1, sizeof(*channel));
1075 channel->channel_name = strdup(channel_name);
1076 channel->id = channel_id;
1077 channel->mode = mode;
1078 channel->user_list = silc_hash_table_alloc(1, silc_hash_ptr, NULL, NULL,
1079 NULL, NULL, NULL, TRUE);
1081 /* Put it to the ID cache */
1082 if (!silc_idcache_add(conn->internal->channel_cache, channel->channel_name,
1083 (void *)channel->id, (void *)channel, 0, NULL)) {
1084 silc_free(channel->channel_name);
1085 silc_hash_table_free(channel->user_list);
1093 /* Foreach callbcak to free all users from the channel when deleting a
1096 static void silc_client_del_channel_foreach(void *key, void *context,
1099 SilcChannelUser chu = (SilcChannelUser)context;
1101 SILC_LOG_DEBUG(("Start"));
1103 /* Remove the context from the client's channel hash table as that
1104 table and channel's user_list hash table share this same context. */
1105 silc_hash_table_del(chu->client->channels, chu->channel);
1109 /* Removes channel from the cache by the channel entry. */
1111 bool silc_client_del_channel(SilcClient client, SilcClientConnection conn,
1112 SilcChannelEntry channel)
1114 bool ret = silc_idcache_del_by_context(conn->internal->channel_cache,
1117 SILC_LOG_DEBUG(("Start"));
1119 /* Free all client entrys from the users list. The silc_hash_table_free
1120 will free all the entries so they are not freed at the foreach
1122 silc_hash_table_foreach(channel->user_list, silc_client_del_channel_foreach,
1124 silc_hash_table_free(channel->user_list);
1126 silc_free(channel->channel_name);
1127 silc_free(channel->topic);
1128 silc_free(channel->id);
1129 if (channel->founder_key)
1130 silc_pkcs_public_key_free(channel->founder_key);
1131 silc_free(channel->key);
1132 if (channel->channel_key)
1133 silc_cipher_free(channel->channel_key);
1135 silc_hmac_free(channel->hmac);
1136 if (channel->old_channel_keys) {
1138 silc_dlist_start(channel->old_channel_keys);
1139 while ((key = silc_dlist_get(channel->old_channel_keys)) != SILC_LIST_END)
1140 silc_cipher_free(key);
1141 silc_dlist_uninit(channel->old_channel_keys);
1143 if (channel->old_hmacs) {
1145 silc_dlist_start(channel->old_hmacs);
1146 while ((hmac = silc_dlist_get(channel->old_hmacs)) != SILC_LIST_END)
1147 silc_hmac_free(hmac);
1148 silc_dlist_uninit(channel->old_hmacs);
1150 silc_schedule_task_del_by_context(conn->client->schedule, channel);
1151 silc_client_del_channel_private_keys(client, conn, channel);
1156 /* Replaces the channel ID of the `channel' to `new_id'. Returns FALSE
1157 if the ID could not be changed. */
1159 bool silc_client_replace_channel_id(SilcClient client,
1160 SilcClientConnection conn,
1161 SilcChannelEntry channel,
1162 SilcChannelID *new_id)
1167 SILC_LOG_DEBUG(("Old Channel ID id(%s)",
1168 silc_id_render(channel->id, SILC_ID_CHANNEL)));
1169 SILC_LOG_DEBUG(("New Channel ID id(%s)",
1170 silc_id_render(new_id, SILC_ID_CHANNEL)));
1172 silc_idcache_del_by_id(conn->internal->channel_cache, channel->id);
1173 silc_free(channel->id);
1174 channel->id = new_id;
1175 return silc_idcache_add(conn->internal->channel_cache,
1176 channel->channel_name,
1177 (void *)channel->id, (void *)channel, 0, NULL);
1181 /* Finds entry for channel by the channel name. Returns the entry or NULL
1182 if the entry was not found. It is found only if the client is joined
1185 SilcChannelEntry silc_client_get_channel(SilcClient client,
1186 SilcClientConnection conn,
1189 SilcIDCacheEntry id_cache;
1190 SilcChannelEntry entry;
1192 assert(client && conn);
1196 SILC_LOG_DEBUG(("Start"));
1198 if (!silc_idcache_find_by_name_one(conn->internal->channel_cache, channel,
1202 entry = (SilcChannelEntry)id_cache->context;
1204 SILC_LOG_DEBUG(("Found"));
1209 /* Finds entry for channel by the channel ID. Returns the entry or NULL
1210 if the entry was not found. It is found only if the client is joined
1213 SilcChannelEntry silc_client_get_channel_by_id(SilcClient client,
1214 SilcClientConnection conn,
1215 SilcChannelID *channel_id)
1217 SilcIDCacheEntry id_cache;
1218 SilcChannelEntry entry;
1220 assert(client && conn);
1224 SILC_LOG_DEBUG(("Start"));
1226 if (!silc_idcache_find_by_id_one(conn->internal->channel_cache, channel_id,
1230 entry = (SilcChannelEntry)id_cache->context;
1232 SILC_LOG_DEBUG(("Found"));
1239 SilcClientConnection conn;
1241 SilcChannelID *channel_id;
1244 SilcGetChannelCallback completion;
1246 } *GetChannelInternal;
1248 SILC_CLIENT_CMD_FUNC(get_channel_resolve_callback)
1250 GetChannelInternal i = (GetChannelInternal)context;
1251 SilcChannelEntry entry;
1253 SILC_LOG_DEBUG(("Start"));
1255 /* Get the channel */
1256 entry = silc_client_get_channel(i->client, i->conn, i->u.channel_name);
1258 i->completion(i->client, i->conn, &entry, 1, i->context);
1260 i->completion(i->client, i->conn, NULL, 0, i->context);
1263 silc_free(i->u.channel_name);
1267 /* Resolves channel entry from the server by the channel name. */
1269 void silc_client_get_channel_resolve(SilcClient client,
1270 SilcClientConnection conn,
1272 SilcGetChannelCallback completion,
1275 GetChannelInternal i = silc_calloc(1, sizeof(*i));
1277 assert(client && conn && channel_name);
1279 SILC_LOG_DEBUG(("Start"));
1283 i->u.channel_name = strdup(channel_name);
1284 i->completion = completion;
1285 i->context = context;
1287 /* Register our own command reply for this command */
1288 silc_client_command_register(client, SILC_COMMAND_IDENTIFY, NULL, NULL,
1289 silc_client_command_reply_identify_i, 0,
1292 /* Send the command */
1293 silc_client_command_send(client, conn, SILC_COMMAND_IDENTIFY,
1295 1, 3, channel_name, strlen(channel_name));
1297 /* Add pending callback */
1298 silc_client_command_pending(conn, SILC_COMMAND_IDENTIFY, conn->cmd_ident,
1299 silc_client_command_get_channel_resolve_callback,
1303 SILC_CLIENT_CMD_FUNC(get_channel_by_id_callback)
1305 GetChannelInternal i = (GetChannelInternal)context;
1306 SilcChannelEntry entry;
1308 SILC_LOG_DEBUG(("Start"));
1310 /* Get the channel */
1311 entry = silc_client_get_channel_by_id(i->client, i->conn, i->u.channel_id);
1313 i->completion(i->client, i->conn, &entry, 1, i->context);
1315 i->completion(i->client, i->conn, NULL, 0, i->context);
1318 silc_free(i->u.channel_id);
1322 /* Resolves channel information from the server by the channel ID. */
1324 void silc_client_get_channel_by_id_resolve(SilcClient client,
1325 SilcClientConnection conn,
1326 SilcChannelID *channel_id,
1327 SilcGetChannelCallback completion,
1331 GetChannelInternal i = silc_calloc(1, sizeof(*i));
1333 assert(client && conn && channel_id);
1335 SILC_LOG_DEBUG(("Start"));
1339 i->u.channel_id = silc_id_dup(channel_id, SILC_ID_CHANNEL);
1340 i->completion = completion;
1341 i->context = context;
1343 /* Register our own command reply for this command */
1344 silc_client_command_register(client, SILC_COMMAND_IDENTIFY, NULL, NULL,
1345 silc_client_command_reply_identify_i, 0,
1348 /* Send the command */
1349 idp = silc_id_payload_encode(channel_id, SILC_ID_CHANNEL);
1350 silc_client_command_send(client, conn, SILC_COMMAND_IDENTIFY,
1352 1, 5, idp->data, idp->len);
1353 silc_buffer_free(idp);
1355 /* Add pending callback */
1356 silc_client_command_pending(conn, SILC_COMMAND_IDENTIFY, conn->cmd_ident,
1357 silc_client_command_get_channel_by_id_callback,
1361 /* Finds entry for server by the server name. */
1363 SilcServerEntry silc_client_get_server(SilcClient client,
1364 SilcClientConnection conn,
1367 SilcIDCacheEntry id_cache;
1368 SilcServerEntry entry;
1370 assert(client && conn);
1374 SILC_LOG_DEBUG(("Start"));
1376 if (!silc_idcache_find_by_name_one(conn->internal->server_cache,
1377 server_name, &id_cache))
1380 entry = (SilcServerEntry)id_cache->context;
1385 /* Finds entry for server by the server ID. */
1387 SilcServerEntry silc_client_get_server_by_id(SilcClient client,
1388 SilcClientConnection conn,
1389 SilcServerID *server_id)
1391 SilcIDCacheEntry id_cache;
1392 SilcServerEntry entry;
1394 assert(client && conn);
1398 SILC_LOG_DEBUG(("Start"));
1400 if (!silc_idcache_find_by_id_one(conn->internal->server_cache,
1401 (void *)server_id, &id_cache))
1404 entry = (SilcServerEntry)id_cache->context;
1409 /* Add new server entry */
1411 SilcServerEntry silc_client_add_server(SilcClient client,
1412 SilcClientConnection conn,
1413 const char *server_name,
1414 const char *server_info,
1415 SilcServerID *server_id)
1417 SilcServerEntry server_entry;
1419 SILC_LOG_DEBUG(("Start"));
1421 server_entry = silc_calloc(1, sizeof(*server_entry));
1422 if (!server_entry || !server_id)
1425 server_entry->server_id = server_id;
1427 server_entry->server_name = strdup(server_name);
1429 server_entry->server_info = strdup(server_info);
1431 /* Add server to cache */
1432 if (!silc_idcache_add(conn->internal->server_cache,
1433 server_entry->server_name,
1434 server_entry->server_id, server_entry, 0, NULL)) {
1435 silc_free(server_entry->server_id);
1436 silc_free(server_entry->server_name);
1437 silc_free(server_entry->server_info);
1438 silc_free(server_entry);
1442 return server_entry;
1445 /* Removes server from the cache by the server entry. */
1447 bool silc_client_del_server(SilcClient client, SilcClientConnection conn,
1448 SilcServerEntry server)
1450 bool ret = silc_idcache_del_by_context(conn->internal->server_cache, server);
1451 silc_free(server->server_name);
1452 silc_free(server->server_info);
1453 silc_free(server->server_id);
1458 /* Updates the `server_entry' with the new information sent as argument. */
1460 void silc_client_update_server(SilcClient client,
1461 SilcClientConnection conn,
1462 SilcServerEntry server_entry,
1463 const char *server_name,
1464 const char *server_info)
1466 SILC_LOG_DEBUG(("Start"));
1469 (!server_entry->server_name ||
1470 !silc_utf8_strcasecmp(server_entry->server_name, server_name))) {
1472 silc_idcache_del_by_context(conn->internal->server_cache, server_entry);
1473 silc_free(server_entry->server_name);
1474 server_entry->server_name = strdup(server_name);
1475 silc_idcache_add(conn->internal->server_cache, server_entry->server_name,
1476 server_entry->server_id,
1477 server_entry, 0, NULL);
1481 (!server_entry->server_info ||
1482 !silc_utf8_strcasecmp(server_entry->server_info, server_info))) {
1483 silc_free(server_entry->server_info);
1484 server_entry->server_info = strdup(server_info);
1488 /* Formats the nickname of the client specified by the `client_entry'.
1489 If the format is specified by the application this will format the
1490 nickname and replace the old nickname in the client entry. If the
1491 format string is not specified then this function has no effect. */
1493 void silc_client_nickname_format(SilcClient client,
1494 SilcClientConnection conn,
1495 SilcClientEntry client_entry)
1498 char *newnick = NULL;
1499 int i, off = 0, len;
1501 SilcClientEntry *clients;
1502 SilcUInt32 clients_count = 0;
1503 SilcClientEntry unformatted = NULL;
1505 SILC_LOG_DEBUG(("Start"));
1507 if (!client->internal->params->nickname_format[0])
1510 if (!client_entry->nickname)
1513 /* Get all clients with same nickname. Do not perform the formatting
1514 if there aren't any clients with same nickname unless the application
1515 is forcing us to do so. */
1516 clients = silc_client_get_clients_local(client, conn,
1517 client_entry->nickname, NULL,
1519 if (!clients && !client->internal->params->nickname_force_format)
1524 for (i = 0; i < clients_count; i++) {
1525 if (clients[i]->valid && clients[i] != client_entry)
1527 if (clients[i]->valid && clients[i] != client_entry &&
1528 silc_utf8_strcasecmp(clients[i]->nickname, client_entry->nickname))
1531 if (!len || freebase)
1534 if (clients_count == 1)
1535 unformatted = clients[0];
1537 for (i = 0; i < clients_count; i++)
1538 if (silc_utf8_strncasecmp(clients[i]->nickname, client_entry->nickname,
1539 strlen(clients[i]->nickname)))
1540 unformatted = clients[i];
1542 /* If we are changing nickname of our local entry we'll enforce
1543 that we will always get the unformatted nickname. Give our
1544 format number to the one that is not formatted now. */
1545 if (unformatted && client_entry == conn->local_entry)
1546 client_entry = unformatted;
1548 cp = client->internal->params->nickname_format;
1558 if (!client_entry->nickname)
1560 len = strlen(client_entry->nickname);
1561 newnick = silc_realloc(newnick, sizeof(*newnick) * (off + len));
1562 memcpy(&newnick[off], client_entry->nickname, len);
1566 /* Stripped hostname */
1567 if (!client_entry->hostname)
1569 len = strcspn(client_entry->hostname, ".");
1570 i = strcspn(client_entry->hostname, "-");
1573 newnick = silc_realloc(newnick, sizeof(*newnick) * (off + len));
1574 memcpy(&newnick[off], client_entry->hostname, len);
1579 if (!client_entry->hostname)
1581 len = strlen(client_entry->hostname);
1582 newnick = silc_realloc(newnick, sizeof(*newnick) * (off + len));
1583 memcpy(&newnick[off], client_entry->hostname, len);
1587 /* Stripped server name */
1588 if (!client_entry->server)
1590 len = strcspn(client_entry->server, ".");
1591 newnick = silc_realloc(newnick, sizeof(*newnick) * (off + len));
1592 memcpy(&newnick[off], client_entry->server, len);
1596 /* Full server name */
1597 if (!client_entry->server)
1599 len = strlen(client_entry->server);
1600 newnick = silc_realloc(newnick, sizeof(*newnick) * (off + len));
1601 memcpy(&newnick[off], client_entry->server, len);
1605 /* Ascending number */
1610 if (clients_count == 1)
1613 for (i = 0; i < clients_count; i++) {
1614 if (!silc_utf8_strncasecmp(clients[i]->nickname, newnick, off))
1616 if (strlen(clients[i]->nickname) <= off)
1618 num = atoi(&clients[i]->nickname[off]);
1623 memset(tmp, 0, sizeof(tmp));
1624 snprintf(tmp, sizeof(tmp) - 1, "%d", ++max);
1626 newnick = silc_realloc(newnick, sizeof(*newnick) * (off + len));
1627 memcpy(&newnick[off], tmp, len);
1632 /* Some other character in the string */
1633 newnick = silc_realloc(newnick, sizeof(*newnick) * (off + 1));
1634 memcpy(&newnick[off], cp, 1);
1642 newnick = silc_realloc(newnick, sizeof(*newnick) * (off + 1));
1645 silc_free(client_entry->nickname);
1646 client_entry->nickname = newnick;