5 Author: Pekka Riikonen <priikone@silcnet.org>
7 Copyright (C) 2001 - 2002 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);
293 SilcClientConnection conn;
294 SilcUInt32 list_count;
295 SilcBuffer client_id_list;
296 SilcGetClientCallback completion;
299 } *GetClientsByListInternal;
301 SILC_CLIENT_CMD_FUNC(get_clients_list_callback)
303 GetClientsByListInternal i = (GetClientsByListInternal)context;
304 SilcIDCacheEntry id_cache = NULL;
305 SilcBuffer client_id_list = i->client_id_list;
306 SilcClientEntry *clients = NULL;
307 SilcUInt32 clients_count = 0;
311 SILC_LOG_DEBUG(("Start"));
319 SILC_LOG_DEBUG(("Resolved all clients"));
321 for (c = 0; c < i->list_count; c++) {
323 SilcClientID *client_id;
326 SILC_GET16_MSB(idp_len, client_id_list->data + 2);
328 client_id = silc_id_payload_parse_id(client_id_list->data, idp_len, NULL);
330 silc_buffer_pull(client_id_list, idp_len);
334 /* Get the client entry */
335 if (silc_idcache_find_by_id_one_ext(i->conn->internal->client_cache,
338 silc_hash_client_id_compare, NULL,
340 clients = silc_realloc(clients, sizeof(*clients) *
341 (clients_count + 1));
342 clients[clients_count] = (SilcClientEntry)id_cache->context;
347 silc_free(client_id);
348 silc_buffer_pull(client_id_list, idp_len);
352 i->completion(i->client, i->conn, clients, clients_count, i->context);
355 i->completion(i->client, i->conn, NULL, 0, i->context);
358 if (i->client_id_list)
359 silc_buffer_free(i->client_id_list);
363 /* Gets client entries by the list of client ID's `client_id_list'. This
364 always resolves those client ID's it does not know yet from the server
365 so this function might take a while. The `client_id_list' is a list
366 of ID Payloads added one after other. JOIN command reply and USERS
367 command reply for example returns this sort of list. The `completion'
368 will be called after the entries are available. */
370 void silc_client_get_clients_by_list(SilcClient client,
371 SilcClientConnection conn,
372 SilcUInt32 list_count,
373 SilcBuffer client_id_list,
374 SilcGetClientCallback completion,
377 SilcIDCacheEntry id_cache = NULL;
379 unsigned char **res_argv = NULL;
380 SilcUInt32 *res_argv_lens = NULL, *res_argv_types = NULL, res_argc = 0;
381 GetClientsByListInternal in;
382 bool wait_res = FALSE;
384 assert(client && conn && client_id_list);
386 SILC_LOG_DEBUG(("Start"));
388 in = silc_calloc(1, sizeof(*in));
391 in->list_count = list_count;
392 in->client_id_list = silc_buffer_copy(client_id_list);
393 in->completion = completion;
394 in->context = context;
396 for (i = 0; i < list_count; i++) {
398 SilcClientID *client_id;
399 SilcClientEntry entry;
403 SILC_GET16_MSB(idp_len, client_id_list->data + 2);
405 client_id = silc_id_payload_parse_id(client_id_list->data, idp_len, NULL);
407 silc_buffer_pull(client_id_list, idp_len);
411 /* Check if we have this client cached already. */
413 silc_idcache_find_by_id_one_ext(conn->internal->client_cache,
414 (void *)client_id, NULL, NULL,
415 silc_hash_client_id_compare, NULL,
418 /* If we don't have the entry or it has incomplete info, then resolve
419 it from the server. */
420 if (!ret || !((SilcClientEntry)id_cache->context)->nickname) {
421 entry = ret ? (SilcClientEntry)id_cache->context : NULL;
424 if (entry->status & SILC_CLIENT_STATUS_RESOLVING) {
425 /* Attach to this resolving and wait until it finishes */
426 silc_client_command_pending(
427 conn, SILC_COMMAND_NONE,
428 entry->resolve_cmd_ident,
429 silc_client_command_get_clients_list_callback,
434 silc_free(client_id);
435 silc_buffer_pull(client_id_list, idp_len);
439 entry->status |= SILC_CLIENT_STATUS_RESOLVING;
440 entry->resolve_cmd_ident = conn->cmd_ident + 1;
443 /* No we don't have it, query it from the server. Assemble argument
444 table that will be sent for the IDENTIFY command later. */
445 res_argv = silc_realloc(res_argv, sizeof(*res_argv) *
447 res_argv_lens = silc_realloc(res_argv_lens, sizeof(*res_argv_lens) *
449 res_argv_types = silc_realloc(res_argv_types, sizeof(*res_argv_types) *
451 res_argv[res_argc] = client_id_list->data;
452 res_argv_lens[res_argc] = idp_len;
453 res_argv_types[res_argc] = res_argc + 5;
457 silc_free(client_id);
458 silc_buffer_pull(client_id_list, idp_len);
461 silc_buffer_push(client_id_list, client_id_list->data -
462 client_id_list->head);
464 /* Query the client information from server if the list included clients
465 that we don't know about. */
469 /* Send the IDENTIFY command to server */
470 res_cmd = silc_command_payload_encode(SILC_COMMAND_IDENTIFY,
471 res_argc, res_argv, res_argv_lens,
472 res_argv_types, ++conn->cmd_ident);
473 silc_client_packet_send(client, conn->sock, SILC_PACKET_COMMAND,
474 NULL, 0, NULL, NULL, res_cmd->data, res_cmd->len,
477 /* Register our own command reply for this command */
478 silc_client_command_register(client, SILC_COMMAND_IDENTIFY, NULL, NULL,
479 silc_client_command_reply_identify_i, 0,
482 /* Process the applications request after reply has been received */
483 silc_client_command_pending(conn, SILC_COMMAND_IDENTIFY, conn->cmd_ident,
484 silc_client_command_get_clients_list_callback,
488 silc_buffer_free(res_cmd);
490 silc_free(res_argv_lens);
491 silc_free(res_argv_types);
498 /* We have the clients in cache, get them and call the completion */
499 silc_client_command_get_clients_list_callback((void *)in, NULL);
502 /* Finds entry for client by the client's ID. Returns the entry or NULL
503 if the entry was not found. */
505 SilcClientEntry silc_client_get_client_by_id(SilcClient client,
506 SilcClientConnection conn,
507 SilcClientID *client_id)
509 SilcIDCacheEntry id_cache;
511 assert(client && conn);
515 SILC_LOG_DEBUG(("Finding client by ID (%s)",
516 silc_id_render(client_id, SILC_ID_CLIENT)));
518 /* Find ID from cache */
519 if (!silc_idcache_find_by_id_one_ext(conn->internal->client_cache,
520 (void *)client_id, NULL, NULL,
521 silc_hash_client_id_compare, NULL,
525 SILC_LOG_DEBUG(("Found"));
527 return (SilcClientEntry)id_cache->context;
532 SilcClientConnection conn;
533 SilcClientID *client_id;
534 SilcGetClientCallback completion;
536 } *GetClientByIDInternal;
538 SILC_CLIENT_CMD_FUNC(get_client_by_id_callback)
540 GetClientByIDInternal i = (GetClientByIDInternal)context;
541 SilcClientEntry entry;
544 entry = silc_client_get_client_by_id(i->client, i->conn, i->client_id);
547 i->completion(i->client, i->conn, &entry, 1, i->context);
550 i->completion(i->client, i->conn, NULL, 0, i->context);
553 silc_free(i->client_id);
557 /* Same as above but will always resolve the information from the server.
558 Use this only if you know that you don't have the entry and the only
559 thing you know about the client is its ID. */
561 void silc_client_get_client_by_id_resolve(SilcClient client,
562 SilcClientConnection conn,
563 SilcClientID *client_id,
564 SilcBuffer attributes,
565 SilcGetClientCallback completion,
569 GetClientByIDInternal i = silc_calloc(1, sizeof(*i));
571 assert(client && conn && client_id);
573 SILC_LOG_DEBUG(("Start"));
577 i->client_id = silc_id_dup(client_id, SILC_ID_CLIENT);
578 i->completion = completion;
579 i->context = context;
581 /* Register our own command reply for this command */
582 silc_client_command_register(client, SILC_COMMAND_WHOIS, NULL, NULL,
583 silc_client_command_reply_whois_i, 0,
586 /* Send the command */
587 idp = silc_id_payload_encode(client_id, SILC_ID_CLIENT);
588 silc_client_command_send(client, conn, SILC_COMMAND_WHOIS, conn->cmd_ident,
589 2, 3, attributes ? attributes->data : NULL,
590 attributes ? attributes->len : 0,
591 4, idp->data, idp->len);
592 silc_buffer_free(idp);
594 /* Add pending callback */
595 silc_client_command_pending(conn, SILC_COMMAND_WHOIS, conn->cmd_ident,
596 silc_client_command_get_client_by_id_callback,
601 /******************************************************************************
603 Client, Channel and Server entry manipulation
605 ******************************************************************************/
608 /* Creates new client entry and adds it to the ID cache. Returns pointer
612 silc_client_add_client(SilcClient client, SilcClientConnection conn,
613 char *nickname, char *username,
614 char *userinfo, SilcClientID *id, SilcUInt32 mode)
616 SilcClientEntry client_entry;
619 SILC_LOG_DEBUG(("Start"));
621 /* Save the client infos */
622 client_entry = silc_calloc(1, sizeof(*client_entry));
623 client_entry->id = id;
624 client_entry->valid = TRUE;
625 silc_parse_userfqdn(nickname, &nick, &client_entry->server);
626 silc_parse_userfqdn(username, &client_entry->username,
627 &client_entry->hostname);
629 client_entry->realname = strdup(userinfo);
630 client_entry->mode = mode;
632 client_entry->nickname = strdup(nick);
633 client_entry->channels = silc_hash_table_alloc(1, silc_hash_ptr, NULL, NULL,
634 NULL, NULL, NULL, TRUE);
636 /* Format the nickname */
637 silc_client_nickname_format(client, conn, client_entry);
639 /* Add client to cache, the non-formatted nickname is saved to cache */
640 if (!silc_idcache_add(conn->internal->client_cache, nick, client_entry->id,
641 (void *)client_entry, 0, NULL)) {
642 silc_free(client_entry->nickname);
643 silc_free(client_entry->username);
644 silc_free(client_entry->hostname);
645 silc_free(client_entry->server);
646 silc_hash_table_free(client_entry->channels);
647 silc_free(client_entry);
654 /* Updates the `client_entry' with the new information sent as argument. */
656 void silc_client_update_client(SilcClient client,
657 SilcClientConnection conn,
658 SilcClientEntry client_entry,
659 const char *nickname,
660 const char *username,
661 const char *userinfo,
666 SILC_LOG_DEBUG(("Start"));
668 if ((!client_entry->username || !client_entry->hostname) && username) {
669 silc_free(client_entry->username);
670 silc_free(client_entry->hostname);
671 client_entry->username = NULL;
672 client_entry->hostname = NULL;
673 silc_parse_userfqdn(username, &client_entry->username,
674 &client_entry->hostname);
676 if (!client_entry->realname && userinfo)
677 client_entry->realname = strdup(userinfo);
678 if (!client_entry->nickname && nickname) {
679 silc_parse_userfqdn(nickname, &nick, &client_entry->server);
680 client_entry->nickname = strdup(nick);
681 silc_client_nickname_format(client, conn, client_entry);
683 client_entry->mode = mode;
686 /* Remove the old cache entry and create a new one */
687 silc_idcache_del_by_context(conn->internal->client_cache, client_entry);
688 silc_idcache_add(conn->internal->client_cache, nick, client_entry->id,
689 client_entry, 0, NULL);
693 /* Deletes the client entry and frees all memory. */
695 void silc_client_del_client_entry(SilcClient client,
696 SilcClientConnection conn,
697 SilcClientEntry client_entry)
699 SILC_LOG_DEBUG(("Start"));
701 silc_free(client_entry->nickname);
702 silc_free(client_entry->username);
703 silc_free(client_entry->realname);
704 silc_free(client_entry->hostname);
705 silc_free(client_entry->server);
706 silc_free(client_entry->id);
707 silc_free(client_entry->fingerprint);
708 silc_hash_table_free(client_entry->channels);
709 if (client_entry->send_key)
710 silc_cipher_free(client_entry->send_key);
711 if (client_entry->receive_key)
712 silc_cipher_free(client_entry->receive_key);
713 silc_free(client_entry->key);
714 silc_client_ftp_session_free_client(conn, client_entry);
715 if (client_entry->ke)
716 silc_client_abort_key_agreement(client, conn, client_entry);
717 silc_free(client_entry);
720 /* Removes client from the cache by the client entry. */
722 bool silc_client_del_client(SilcClient client, SilcClientConnection conn,
723 SilcClientEntry client_entry)
725 bool ret = silc_idcache_del_by_context(conn->internal->client_cache,
728 /* Remove from channels */
729 silc_client_remove_from_channels(client, conn, client_entry);
731 /* Free the client entry data */
732 silc_client_del_client_entry(client, conn, client_entry);
737 /* Add new channel entry to the ID Cache */
739 SilcChannelEntry silc_client_add_channel(SilcClient client,
740 SilcClientConnection conn,
741 const char *channel_name,
743 SilcChannelID *channel_id)
745 SilcChannelEntry channel;
747 SILC_LOG_DEBUG(("Start"));
749 channel = silc_calloc(1, sizeof(*channel));
750 channel->channel_name = strdup(channel_name);
751 channel->id = channel_id;
752 channel->mode = mode;
753 channel->user_list = silc_hash_table_alloc(1, silc_hash_ptr, NULL, NULL,
754 NULL, NULL, NULL, TRUE);
756 /* Put it to the ID cache */
757 if (!silc_idcache_add(conn->internal->channel_cache, channel->channel_name,
758 (void *)channel->id, (void *)channel, 0, NULL)) {
759 silc_free(channel->channel_name);
760 silc_hash_table_free(channel->user_list);
768 /* Foreach callbcak to free all users from the channel when deleting a
771 static void silc_client_del_channel_foreach(void *key, void *context,
774 SilcChannelUser chu = (SilcChannelUser)context;
776 SILC_LOG_DEBUG(("Start"));
778 /* Remove the context from the client's channel hash table as that
779 table and channel's user_list hash table share this same context. */
780 silc_hash_table_del(chu->client->channels, chu->channel);
784 /* Removes channel from the cache by the channel entry. */
786 bool silc_client_del_channel(SilcClient client, SilcClientConnection conn,
787 SilcChannelEntry channel)
789 bool ret = silc_idcache_del_by_context(conn->internal->channel_cache,
792 SILC_LOG_DEBUG(("Start"));
794 /* Free all client entrys from the users list. The silc_hash_table_free
795 will free all the entries so they are not freed at the foreach
797 silc_hash_table_foreach(channel->user_list, silc_client_del_channel_foreach,
799 silc_hash_table_free(channel->user_list);
801 silc_free(channel->channel_name);
802 silc_free(channel->id);
803 silc_free(channel->key);
804 if (channel->channel_key)
805 silc_cipher_free(channel->channel_key);
807 silc_hmac_free(channel->hmac);
808 if (channel->old_channel_key)
809 silc_cipher_free(channel->old_channel_key);
810 if (channel->old_hmac)
811 silc_hmac_free(channel->old_hmac);
812 if (channel->rekey_task)
813 silc_schedule_task_del(conn->client->schedule, channel->rekey_task);
814 silc_client_del_channel_private_keys(client, conn, channel);
819 /* Replaces the channel ID of the `channel' to `new_id'. Returns FALSE
820 if the ID could not be changed. */
822 bool silc_client_replace_channel_id(SilcClient client,
823 SilcClientConnection conn,
824 SilcChannelEntry channel,
825 SilcChannelID *new_id)
830 SILC_LOG_DEBUG(("Old Channel ID id(%s)",
831 silc_id_render(channel->id, SILC_ID_CHANNEL)));
832 SILC_LOG_DEBUG(("New Channel ID id(%s)",
833 silc_id_render(new_id, SILC_ID_CHANNEL)));
835 silc_idcache_del_by_id(conn->internal->channel_cache, channel->id);
836 silc_free(channel->id);
837 channel->id = new_id;
838 return silc_idcache_add(conn->internal->channel_cache,
839 channel->channel_name,
840 (void *)channel->id, (void *)channel, 0, NULL);
844 /* Finds entry for channel by the channel name. Returns the entry or NULL
845 if the entry was not found. It is found only if the client is joined
848 SilcChannelEntry silc_client_get_channel(SilcClient client,
849 SilcClientConnection conn,
852 SilcIDCacheEntry id_cache;
853 SilcChannelEntry entry;
855 assert(client && conn);
859 SILC_LOG_DEBUG(("Start"));
861 if (!silc_idcache_find_by_name_one(conn->internal->channel_cache, channel,
865 entry = (SilcChannelEntry)id_cache->context;
867 SILC_LOG_DEBUG(("Found"));
872 /* Finds entry for channel by the channel ID. Returns the entry or NULL
873 if the entry was not found. It is found only if the client is joined
876 SilcChannelEntry silc_client_get_channel_by_id(SilcClient client,
877 SilcClientConnection conn,
878 SilcChannelID *channel_id)
880 SilcIDCacheEntry id_cache;
881 SilcChannelEntry entry;
883 assert(client && conn);
887 SILC_LOG_DEBUG(("Start"));
889 if (!silc_idcache_find_by_id_one(conn->internal->channel_cache, channel_id,
893 entry = (SilcChannelEntry)id_cache->context;
895 SILC_LOG_DEBUG(("Found"));
902 SilcClientConnection conn;
903 SilcChannelID *channel_id;
904 SilcGetChannelCallback completion;
906 } *GetChannelByIDInternal;
908 SILC_CLIENT_CMD_FUNC(get_channel_by_id_callback)
910 GetChannelByIDInternal i = (GetChannelByIDInternal)context;
911 SilcChannelEntry entry;
913 SILC_LOG_DEBUG(("Start"));
915 /* Get the channel */
916 entry = silc_client_get_channel_by_id(i->client, i->conn, i->channel_id);
918 i->completion(i->client, i->conn, &entry, 1, i->context);
920 i->completion(i->client, i->conn, NULL, 0, i->context);
923 silc_free(i->channel_id);
927 /* Resolves channel information from the server by the channel ID. */
929 void silc_client_get_channel_by_id_resolve(SilcClient client,
930 SilcClientConnection conn,
931 SilcChannelID *channel_id,
932 SilcGetChannelCallback completion,
936 GetChannelByIDInternal i = silc_calloc(1, sizeof(*i));
938 assert(client && conn && channel_id);
940 SILC_LOG_DEBUG(("Start"));
944 i->channel_id = silc_id_dup(channel_id, SILC_ID_CHANNEL);
945 i->completion = completion;
946 i->context = context;
948 /* Register our own command reply for this command */
949 silc_client_command_register(client, SILC_COMMAND_IDENTIFY, NULL, NULL,
950 silc_client_command_reply_identify_i, 0,
953 /* Send the command */
954 idp = silc_id_payload_encode(channel_id, SILC_ID_CHANNEL);
955 silc_client_command_send(client, conn, SILC_COMMAND_IDENTIFY,
957 1, 5, idp->data, idp->len);
958 silc_buffer_free(idp);
960 /* Add pending callback */
961 silc_client_command_pending(conn, SILC_COMMAND_IDENTIFY, conn->cmd_ident,
962 silc_client_command_get_channel_by_id_callback,
966 /* Finds entry for server by the server name. */
968 SilcServerEntry silc_client_get_server(SilcClient client,
969 SilcClientConnection conn,
972 SilcIDCacheEntry id_cache;
973 SilcServerEntry entry;
975 assert(client && conn);
979 SILC_LOG_DEBUG(("Start"));
981 if (!silc_idcache_find_by_name_one(conn->internal->server_cache,
982 server_name, &id_cache))
985 entry = (SilcServerEntry)id_cache->context;
990 /* Finds entry for server by the server ID. */
992 SilcServerEntry silc_client_get_server_by_id(SilcClient client,
993 SilcClientConnection conn,
994 SilcServerID *server_id)
996 SilcIDCacheEntry id_cache;
997 SilcServerEntry entry;
999 assert(client && conn);
1003 SILC_LOG_DEBUG(("Start"));
1005 if (!silc_idcache_find_by_id_one(conn->internal->server_cache,
1006 (void *)server_id, &id_cache))
1009 entry = (SilcServerEntry)id_cache->context;
1014 /* Add new server entry */
1016 SilcServerEntry silc_client_add_server(SilcClient client,
1017 SilcClientConnection conn,
1018 const char *server_name,
1019 const char *server_info,
1020 SilcServerID *server_id)
1022 SilcServerEntry server_entry;
1024 SILC_LOG_DEBUG(("Start"));
1026 server_entry = silc_calloc(1, sizeof(*server_entry));
1027 if (!server_entry || !server_id)
1030 server_entry->server_id = server_id;
1032 server_entry->server_name = strdup(server_name);
1034 server_entry->server_info = strdup(server_info);
1036 /* Add server to cache */
1037 if (!silc_idcache_add(conn->internal->server_cache,
1038 server_entry->server_name,
1039 server_entry->server_id, server_entry, 0, NULL)) {
1040 silc_free(server_entry->server_id);
1041 silc_free(server_entry->server_name);
1042 silc_free(server_entry->server_info);
1043 silc_free(server_entry);
1047 return server_entry;
1050 /* Removes server from the cache by the server entry. */
1052 bool silc_client_del_server(SilcClient client, SilcClientConnection conn,
1053 SilcServerEntry server)
1055 bool ret = silc_idcache_del_by_context(conn->internal->server_cache, server);
1056 silc_free(server->server_name);
1057 silc_free(server->server_info);
1058 silc_free(server->server_id);
1063 /* Updates the `server_entry' with the new information sent as argument. */
1065 void silc_client_update_server(SilcClient client,
1066 SilcClientConnection conn,
1067 SilcServerEntry server_entry,
1068 const char *server_name,
1069 const char *server_info)
1071 SILC_LOG_DEBUG(("Start"));
1073 if (server_name && (!server_entry->server_name ||
1074 strcmp(server_entry->server_name, server_name))) {
1076 silc_idcache_del_by_context(conn->internal->server_cache, server_entry);
1077 silc_free(server_entry->server_name);
1078 server_entry->server_name = strdup(server_name);
1079 silc_idcache_add(conn->internal->server_cache, server_entry->server_name,
1080 server_entry->server_id,
1081 server_entry, 0, NULL);
1084 if (server_info && (!server_entry->server_info ||
1085 strcmp(server_entry->server_info, server_info))) {
1086 silc_free(server_entry->server_info);
1087 server_entry->server_info = strdup(server_info);
1091 /* Formats the nickname of the client specified by the `client_entry'.
1092 If the format is specified by the application this will format the
1093 nickname and replace the old nickname in the client entry. If the
1094 format string is not specified then this function has no effect. */
1096 void silc_client_nickname_format(SilcClient client,
1097 SilcClientConnection conn,
1098 SilcClientEntry client_entry)
1101 char *newnick = NULL;
1102 int i, off = 0, len;
1104 SilcClientEntry *clients;
1105 SilcUInt32 clients_count = 0;
1106 SilcClientEntry unformatted = NULL;
1108 SILC_LOG_DEBUG(("Start"));
1110 if (!client->internal->params->nickname_format[0])
1113 if (!client_entry->nickname)
1116 /* Get all clients with same nickname. Do not perform the formatting
1117 if there aren't any clients with same nickname unless the application
1118 is forcing us to do so. */
1119 clients = silc_client_get_clients_local(client, conn,
1120 client_entry->nickname, NULL,
1122 if (!clients && !client->internal->params->nickname_force_format)
1127 for (i = 0; i < clients_count; i++) {
1128 if (clients[i]->valid && clients[i] != client_entry)
1130 if (clients[i]->valid && clients[i] != client_entry &&
1131 !strcmp(clients[i]->nickname, client_entry->nickname))
1134 if (!len || freebase)
1137 if (clients_count == 1)
1138 unformatted = clients[0];
1140 for (i = 0; i < clients_count; i++)
1141 if (!strncasecmp(clients[i]->nickname, client_entry->nickname,
1142 strlen(clients[i]->nickname)))
1143 unformatted = clients[i];
1145 /* If we are changing nickname of our local entry we'll enforce
1146 that we will always get the unformatted nickname. Give our
1147 format number to the one that is not formatted now. */
1148 if (unformatted && client_entry == conn->local_entry)
1149 client_entry = unformatted;
1151 cp = client->internal->params->nickname_format;
1161 if (!client_entry->nickname)
1163 len = strlen(client_entry->nickname);
1164 newnick = silc_realloc(newnick, sizeof(*newnick) * (off + len));
1165 memcpy(&newnick[off], client_entry->nickname, len);
1169 /* Stripped hostname */
1170 if (!client_entry->hostname)
1172 len = strcspn(client_entry->hostname, ".");
1173 i = strcspn(client_entry->hostname, "-");
1176 newnick = silc_realloc(newnick, sizeof(*newnick) * (off + len));
1177 memcpy(&newnick[off], client_entry->hostname, len);
1182 if (!client_entry->hostname)
1184 len = strlen(client_entry->hostname);
1185 newnick = silc_realloc(newnick, sizeof(*newnick) * (off + len));
1186 memcpy(&newnick[off], client_entry->hostname, len);
1190 /* Stripped server name */
1191 if (!client_entry->server)
1193 len = strcspn(client_entry->server, ".");
1194 newnick = silc_realloc(newnick, sizeof(*newnick) * (off + len));
1195 memcpy(&newnick[off], client_entry->server, len);
1199 /* Full server name */
1200 if (!client_entry->server)
1202 len = strlen(client_entry->server);
1203 newnick = silc_realloc(newnick, sizeof(*newnick) * (off + len));
1204 memcpy(&newnick[off], client_entry->server, len);
1208 /* Ascending number */
1213 if (clients_count == 1)
1216 for (i = 0; i < clients_count; i++) {
1217 if (strncasecmp(clients[i]->nickname, newnick, off))
1219 if (strlen(clients[i]->nickname) <= off)
1221 num = atoi(&clients[i]->nickname[off]);
1226 memset(tmp, 0, sizeof(tmp));
1227 snprintf(tmp, sizeof(tmp) - 1, "%d", ++max);
1229 newnick = silc_realloc(newnick, sizeof(*newnick) * (off + len));
1230 memcpy(&newnick[off], tmp, len);
1235 /* Some other character in the string */
1236 newnick = silc_realloc(newnick, sizeof(*newnick) * (off + 1));
1237 memcpy(&newnick[off], cp, 1);
1245 newnick = silc_realloc(newnick, sizeof(*newnick) * (off + 1));
1248 silc_free(client_entry->nickname);
1249 client_entry->nickname = newnick;