5 Author: Pekka Riikonen <priikone@silcnet.org>
7 Copyright (C) 2001 - 2006 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.
22 #include "silcclient.h"
23 #include "client_internal.h"
27 /************************ Client Searching Locally **************************/
29 /* Finds entry for client by the client's ID. Returns the entry or NULL
30 if the entry was not found. */
32 SilcClientEntry silc_client_get_client_by_id(SilcClient client,
33 SilcClientConnection conn,
34 SilcClientID *client_id)
36 SilcIDCacheEntry id_cache;
37 SilcClientEntry client_entry;
39 if (!client || !conn || !client_id)
42 SILC_LOG_DEBUG(("Finding client by ID (%s)",
43 silc_id_render(client_id, SILC_ID_CLIENT)));
45 silc_mutex_lock(conn->internal->lock);
47 /* Find ID from cache */
48 if (!silc_idcache_find_by_id_one(conn->internal->client_cache, client_id,
52 client_entry = id_cache->context;
55 silc_client_ref_client(client, conn, client_entry);
56 silc_mutex_lock(conn->internal->lock);
58 SILC_LOG_DEBUG(("Found"));
63 /* Finds clients by nickname from local cache. */
65 SilcDList silc_client_get_clients_local(SilcClient client,
66 SilcClientConnection conn,
70 SilcIDCacheEntry id_cache;
73 SilcClientEntry entry;
76 if (!client || !conn || !nickname)
79 /* Normalize nickname for search */
80 nicknamec = silc_identifier_check(nickname, strlen(nickname),
81 SILC_STRING_UTF8, 128, NULL);
86 clients = silc_dlist_init();
92 silc_mutex_lock(conn->internal->lock);
95 if (!silc_idcache_find_by_name(conn->internal->client_cache, nicknamec,
97 silc_mutex_unlock(conn->internal->lock);
99 silc_dlist_uninit(clients);
104 /* Take all without any further checking */
105 silc_list_start(list);
106 while ((id_cache = silc_list_get(list))) {
107 silc_client_ref_client(client, conn, entry);
108 silc_dlist_add(clients, id_cache->context);
111 /* Check multiple cache entries for exact match */
112 silc_list_start(list);
113 while ((id_cache = silc_list_get(list))) {
114 entry = id_cache->context;
115 if (silc_utf8_strcasecmp(entry->nickname, format)) {
116 silc_client_ref_client(client, conn, entry);
117 silc_dlist_add(clients, entry);
122 silc_mutex_unlock(conn->internal->lock);
124 silc_dlist_start(clients);
126 silc_free(nicknamec);
130 /********************** Client Resolving from Server ************************/
132 /* Resolving context */
135 SilcGetClientCallback completion;
137 } *SilcClientGetClientInternal;
139 /* Resolving command callback */
141 static SilcBool silc_client_get_clients_cb(SilcClient client,
142 SilcClientConnection conn,
149 SilcClientGetClientInternal i = context;
150 SilcClientEntry client_entry;
152 if (error != SILC_STATUS_OK) {
153 SILC_LOG_DEBUG(("Resolving failed: %s", silc_get_status_message(error)));
155 i->completion(client, conn, error, NULL, i->context);
159 /* Add the returned client to list */
161 client_entry = va_arg(ap, SilcClientEntry);
162 silc_client_ref_client(client, conn, client_entry);
163 silc_dlist_add(i->clients, client_entry);
166 if (status == SILC_STATUS_OK || status == SILC_STATUS_LIST_END) {
167 /* Deliver the clients to the caller */
169 SILC_LOG_DEBUG(("Resolved %d clients", silc_dlist_count(i->clients)));
170 silc_dlist_start(i->clients);
171 i->completion(client, conn, SILC_STATUS_OK, i->clients, i->context);
179 silc_client_list_free(client, conn, i->clients);
184 /* Resolves client information from server by the client ID. */
186 void silc_client_get_client_by_id_resolve(SilcClient client,
187 SilcClientConnection conn,
188 SilcClientID *client_id,
189 SilcBuffer attributes,
190 SilcGetClientCallback completion,
193 SilcClientGetClientInternal i;
196 if (!client || !conn | !client_id)
199 SILC_LOG_DEBUG(("Resolve client by ID (%s)",
200 silc_id_render(client_id, SILC_ID_CLIENT)));
202 i = silc_calloc(1, sizeof(*i));
205 i->completion = completion;
206 i->context = context;
208 /* Send the command */
209 idp = silc_id_payload_encode(client_id, SILC_ID_CLIENT);
210 silc_client_command_send(client, conn, SILC_COMMAND_WHOIS,
211 silc_client_get_clients_cb, i,
212 2, 3, silc_buffer_datalen(attributes),
213 4, silc_buffer_datalen(idp));
214 silc_buffer_free(idp);
217 /* Finds client entry or entries by the `nickname' and `server'. The
218 completion callback will be called when the client entries has been
219 found. Used internally by the library. */
221 static SilcUInt16 silc_client_get_clients_i(SilcClient client,
222 SilcClientConnection conn,
224 const char *nickname,
226 SilcBuffer attributes,
227 SilcGetClientCallback completion,
230 SilcClientGetClientInternal i;
231 char userhost[768 + 1];
234 SILC_LOG_DEBUG(("Resolve client by %s command",
235 silc_get_command_name(command)));
237 if (!client || !conn)
239 if (!nickname && !attributes)
242 i = silc_calloc(1, sizeof(*i));
245 i->clients = silc_dlist_init();
248 i->completion = completion;
249 i->context = context;
251 memset(userhost, 0, sizeof(userhost));
252 if (nickname && server) {
253 len = strlen(nickname) + strlen(server) + 3;
254 silc_strncat(userhost, len, nickname, strlen(nickname));
255 silc_strncat(userhost, len, "@", 1);
256 silc_strncat(userhost, len, server, strlen(server));
257 } else if (nickname) {
258 silc_strncat(userhost, sizeof(userhost) - 1, nickname, strlen(nickname));
261 /* Send the command */
262 if (command == SILC_COMMAND_IDENTIFY)
263 return silc_client_command_send(client, conn, SILC_COMMAND_IDENTIFY,
264 silc_client_get_clients_cb, i,
265 1, 1, userhost, strlen(userhost));
266 return silc_client_command_send(client, conn, SILC_COMMAND_WHOIS,
267 silc_client_get_clients_cb, i,
268 2, 1, userhost, strlen(userhost),
269 3, silc_buffer_datalen(attributes));
272 /* Get clients from server with IDENTIFY command */
274 SilcUInt16 silc_client_get_clients(SilcClient client,
275 SilcClientConnection conn,
276 const char *nickname,
278 SilcGetClientCallback completion,
281 return silc_client_get_clients_i(client, conn, SILC_COMMAND_IDENTIFY,
282 nickname, server, NULL,
283 completion, context);
286 /* Get clients from server with WHOIS command */
288 SilcUInt16 silc_client_get_clients_whois(SilcClient client,
289 SilcClientConnection conn,
290 const char *nickname,
292 SilcBuffer attributes,
293 SilcGetClientCallback completion,
296 return silc_client_get_clients_i(client, conn, SILC_COMMAND_WHOIS,
297 nickname, server, attributes,
298 completion, context);
301 /* ID list resolving context */
303 SilcGetClientCallback completion;
305 SilcBuffer client_id_list;
306 SilcUInt32 list_count;
307 } *GetClientsByListInternal;
309 static SilcBool silc_client_get_clients_list_cb(SilcClient client,
310 SilcClientConnection conn,
317 GetClientsByListInternal i = context;
318 SilcClientEntry client_entry;
324 /* Process the list after all replies have been received */
325 if (status != SILC_STATUS_OK && !SILC_STATUS_IS_ERROR(status) &&
326 status != SILC_STATUS_LIST_END)
329 SILC_LOG_DEBUG(("Resolved all clients"));
331 clients = silc_dlist_init();
333 status = SILC_STATUS_ERR_RESOURCE_LIMIT;
337 for (c = 0; c < i->list_count; c++) {
339 SILC_GET16_MSB(idp_len, i->client_id_list->data + 2);
341 if (!silc_id_payload_parse_id(i->client_id_list->data, idp_len, &id)) {
342 status = SILC_STATUS_ERR_BAD_CLIENT_ID;
346 /* Get client entry */
347 client_entry = silc_client_get_client_by_id(client, conn, &id.u.client_id);
349 silc_dlist_add(clients, client_entry);
351 if (!silc_buffer_pull(i->client_id_list, idp_len)) {
352 status = SILC_STATUS_ERR_BAD_CLIENT_ID;
357 silc_dlist_start(clients);
358 status = SILC_STATUS_OK;
360 i->completion(client, conn, status, clients, i->context);
363 if (status != SILC_STATUS_OK && i->completion)
364 i->completion(client, conn, status, NULL, i->context);
365 silc_client_list_free(client, conn, clients);
370 /* Gets client entries by the list of client ID's `client_id_list'. This
371 always resolves those client ID's it does not know yet from the server
372 so this function might take a while. The `client_id_list' is a list
373 of ID Payloads added one after other. JOIN command reply and USERS
374 command reply for example returns this sort of list. The `completion'
375 will be called after the entries are available. */
377 void silc_client_get_clients_by_list(SilcClient client,
378 SilcClientConnection conn,
379 SilcUInt32 list_count,
380 SilcBuffer client_id_list,
381 SilcGetClientCallback completion,
384 GetClientsByListInternal in;
385 SilcClientEntry entry;
386 unsigned char **res_argv = NULL;
387 SilcUInt32 *res_argv_lens = NULL, *res_argv_types = NULL, res_argc = 0;
392 SILC_LOG_DEBUG(("Resolve clients from Client ID list"));
394 if (!client || !conn || !client_id_list)
397 in = silc_calloc(1, sizeof(*in));
400 in->completion = completion;
401 in->context = context;
402 in->list_count = list_count;
403 in->client_id_list = silc_buffer_copy(client_id_list);
404 if (!in->client_id_list)
407 for (i = 0; i < list_count; i++) {
409 SILC_GET16_MSB(idp_len, client_id_list->data + 2);
411 if (!silc_id_payload_parse_id(client_id_list->data, idp_len, &id))
414 /* Check if we have this client cached already. If we don't have the
415 entry or it has incomplete info, then resolve it from the server. */
416 entry = silc_client_get_client_by_id(client, conn, &id.u.client_id);
417 if (!entry || !entry->nickname[0] || !entry->username[0] ||
420 res_argv = silc_calloc(list_count, sizeof(*res_argv));
421 res_argv_lens = silc_calloc(list_count, sizeof(*res_argv_lens));
422 res_argv_types = silc_calloc(list_count, sizeof(*res_argv_types));
423 if (!res_argv || !res_argv_lens || !res_argv_types) {
424 silc_client_unref_client(client, conn, entry);
429 res_argv[res_argc] = client_id_list->data;
430 res_argv_lens[res_argc] = idp_len;
431 res_argv_types[res_argc] = res_argc + 5;
434 silc_client_unref_client(client, conn, entry);
436 if (!silc_buffer_pull(client_id_list, idp_len))
439 silc_buffer_start(client_id_list);
441 /* Query the unknown client information from server */
443 silc_client_command_send_argv(client, conn, SILC_COMMAND_WHOIS,
444 silc_client_get_clients_list_cb,
445 in, res_argc, res_argv, res_argv_lens,
448 silc_free(res_argv_lens);
449 silc_free(res_argv_types);
453 /* We have the clients in cache, get them and call the completion */
454 silc_client_get_clients_list_cb(client, conn, SILC_COMMAND_WHOIS,
455 SILC_STATUS_OK, SILC_STATUS_OK, in, NULL);
459 silc_buffer_free(in->client_id_list);
462 silc_free(res_argv_lens);
463 silc_free(res_argv_types);
469 SilcClientConnection conn;
470 SilcChannelID channel_id;
471 SilcGetClientCallback completion;
474 } *GetClientsByChannelInternal;
476 SILC_CLIENT_CMD_FUNC(get_clients_by_channel_cb)
478 GetClientsByChannelInternal i = context;
479 SilcClientEntry *clients = NULL;
480 SilcUInt32 clients_count = 0;
481 SilcBool found = FALSE;
482 SilcChannelEntry channel;
483 SilcHashTableList htl;
492 channel = silc_client_get_channel_by_id(i->client, i->conn, &i->channel_id);
493 if (channel && !silc_hash_table_count(channel->user_list)) {
494 clients = silc_calloc(silc_hash_table_count(channel->user_list),
496 silc_hash_table_list(channel->user_list, &htl);
497 while (silc_hash_table_get(&htl, NULL, (void *)&chu))
498 clients[clients_count++] = chu->client;
499 silc_hash_table_list_reset(&htl);
504 i->completion(i->client, i->conn, clients, clients_count, i->context);
507 i->completion(i->client, i->conn, NULL, 0, i->context);
513 /* Gets client entries by the channel entry indicated by `channel'. Thus,
514 it resolves the clients currently on that channel. */
516 void silc_client_get_clients_by_channel(SilcClient client,
517 SilcClientConnection conn,
518 SilcChannelEntry channel,
519 SilcGetClientCallback completion,
522 GetClientsByChannelInternal in;
523 SilcHashTableList htl;
525 SilcClientEntry entry;
526 unsigned char **res_argv = NULL;
527 SilcUInt32 *res_argv_lens = NULL, *res_argv_types = NULL, res_argc = 0;
529 SilcBool wait_res = FALSE;
531 assert(client && conn && channel);
533 SILC_LOG_DEBUG(("Start"));
535 in = silc_calloc(1, sizeof(*in));
538 in->channel_id = *channel->id;
539 in->completion = completion;
540 in->context = context;
542 /* If user list does not exist, send USERS command. */
543 if (!channel->user_list || !silc_hash_table_count(channel->user_list)) {
544 SILC_LOG_DEBUG(("Sending USERS"));
545 silc_client_command_register(client, SILC_COMMAND_USERS, NULL, NULL,
546 silc_client_command_reply_users_i, 0,
548 silc_client_command_send(client, conn, SILC_COMMAND_USERS,
549 conn->cmd_ident, 1, 2, channel->channel_name,
550 strlen(channel->channel_name));
551 silc_client_command_pending(conn, SILC_COMMAND_USERS, conn->cmd_ident,
552 silc_client_command_get_clients_by_channel_cb,
557 silc_hash_table_list(channel->user_list, &htl);
558 while (silc_hash_table_get(&htl, NULL, (void *)&chu)) {
561 /* If the entry has incomplete info, then resolve it from the server. */
562 if (!entry->nickname[0] || !entry->realname) {
563 if (entry->status & SILC_CLIENT_STATUS_RESOLVING) {
564 /* Attach to this resolving and wait until it finishes */
565 silc_client_command_pending(
566 conn, SILC_COMMAND_NONE,
567 entry->resolve_cmd_ident,
568 silc_client_command_get_clients_by_channel_cb,
574 entry->status |= SILC_CLIENT_STATUS_RESOLVING;
575 entry->resolve_cmd_ident = conn->cmd_ident + 1;
577 idp = silc_id_payload_encode(entry->id, SILC_ID_CLIENT);
579 /* No we don't have it, query it from the server. Assemble argument
580 table that will be sent for the WHOIS command later. */
581 res_argv = silc_realloc(res_argv, sizeof(*res_argv) *
583 res_argv_lens = silc_realloc(res_argv_lens, sizeof(*res_argv_lens) *
585 res_argv_types = silc_realloc(res_argv_types, sizeof(*res_argv_types) *
587 res_argv[res_argc] = silc_memdup(idp->data, idp->len);
588 res_argv_lens[res_argc] = idp->len;
589 res_argv_types[res_argc] = res_argc + 4;
592 silc_buffer_free(idp);
595 silc_hash_table_list_reset(&htl);
597 /* Query the client information from server if the list included clients
598 that we don't know about. */
602 /* Send the WHOIS command to server */
603 res_cmd = silc_command_payload_encode(SILC_COMMAND_WHOIS,
604 res_argc, res_argv, res_argv_lens,
605 res_argv_types, ++conn->cmd_ident);
606 silc_client_packet_send(client, conn->sock, SILC_PACKET_COMMAND,
607 NULL, 0, NULL, NULL, res_cmd->data, res_cmd->len,
610 /* Register our own command reply for this command */
611 silc_client_command_register(client, SILC_COMMAND_WHOIS, NULL, NULL,
612 silc_client_command_reply_whois_i, 0,
615 /* Process the applications request after reply has been received */
616 silc_client_command_pending(
617 conn, SILC_COMMAND_WHOIS, conn->cmd_ident,
618 silc_client_command_get_clients_by_channel_cb,
622 silc_buffer_free(res_cmd);
624 silc_free(res_argv_lens);
625 silc_free(res_argv_types);
632 /* We have the clients in cache, get them and call the completion */
633 silc_client_command_get_clients_by_channel_cb((void *)in, NULL);
638 /************************** Client Entry Routines ***************************/
640 /* Creates new client entry and adds it to the ID cache. Returns pointer
643 SilcClientEntry silc_client_add_client(SilcClient client,
644 SilcClientConnection conn,
645 char *nickname, char *username,
646 char *userinfo, SilcClientID *id,
649 SilcClientEntry client_entry;
652 SILC_LOG_DEBUG(("Adding new client entry"));
654 /* Save the client infos */
655 client_entry = silc_calloc(1, sizeof(*client_entry));
659 client_entry->id = *id;
660 client_entry->internal.valid = TRUE;
661 client_entry->mode = mode;
662 client_entry->realname = userinfo ? strdup(userinfo) : NULL;
663 silc_parse_userfqdn(nickname, client_entry->nickname,
664 sizeof(client_entry->nickname),
665 client_entry->server,
666 sizeof(client_entry->server));
667 silc_parse_userfqdn(username, client_entry->username,
668 sizeof(client_entry->username),
669 client_entry->hostname,
670 sizeof(client_entry->hostname));
671 client_entry->channels = silc_hash_table_alloc(1, silc_hash_ptr, NULL, NULL,
672 NULL, NULL, NULL, TRUE);
673 if (!client_entry->channels) {
674 silc_free(client_entry->realname);
675 silc_free(client_entry);
679 /* Normalize nickname */
680 if (client_entry->nickname[0]) {
681 nick = silc_identifier_check(client_entry->nickname,
682 strlen(client_entry->nickname),
683 SILC_STRING_UTF8, 128, NULL);
685 silc_free(client_entry->realname);
686 silc_hash_table_free(client_entry->channels);
687 silc_free(client_entry);
692 /* Format the nickname */
693 silc_client_nickname_format(client, conn, client_entry);
695 /* Add client to cache, the normalized nickname is saved to cache */
696 if (!silc_idcache_add(conn->internal->client_cache, nick,
697 &client_entry->id, client_entry)) {
699 silc_free(client_entry->realname);
700 silc_hash_table_free(client_entry->channels);
701 silc_free(client_entry);
705 client_entry->nickname_normalized = nick;
710 /* Updates the `client_entry' with the new information sent as argument. */
712 void silc_client_update_client(SilcClient client,
713 SilcClientConnection conn,
714 SilcClientEntry client_entry,
715 const char *nickname,
716 const char *username,
717 const char *userinfo,
722 SILC_LOG_DEBUG(("Update client entry"));
724 if (!client_entry->realname && userinfo)
725 client_entry->realname = strdup(userinfo);
726 if ((!client_entry->username[0] || !client_entry->hostname[0]) && username)
727 silc_parse_userfqdn(username, client_entry->username,
728 sizeof(client_entry->username),
729 client_entry->hostname,
730 sizeof(client_entry->username));
731 if (!client_entry->nickname[0] && nickname) {
732 silc_parse_userfqdn(nickname, client_entry->nickname,
733 sizeof(client_entry->nickname),
734 client_entry->server,
735 sizeof(client_entry->server));
737 /* Normalize nickname */
738 nick = silc_identifier_check(client_entry->nickname,
739 strlen(client_entry->nickname),
740 SILC_STRING_UTF8, 128, NULL);
744 /* Format nickname */
745 silc_client_nickname_format(client, conn, client_entry);
747 /* Remove the old cache entry and create a new one */
748 silc_idcache_del_by_context(conn->internal->client_cache, client_entry,
750 silc_idcache_add(conn->internal->client_cache, nick, &client_entry->id,
753 client_entry->mode = mode;
756 /* Deletes the client entry and frees all memory. */
758 void silc_client_del_client_entry(SilcClient client,
759 SilcClientConnection conn,
760 SilcClientEntry client_entry)
762 SILC_LOG_DEBUG(("Start"));
764 silc_free(client_entry->realname);
765 if (client_entry->public_key)
766 silc_pkcs_public_key_free(client_entry->public_key);
767 silc_hash_table_free(client_entry->channels);
768 if (client_entry->internal.send_key)
769 silc_cipher_free(client_entry->internal.send_key);
770 if (client_entry->internal.receive_key)
771 silc_cipher_free(client_entry->internal.receive_key);
772 silc_free(client_entry->internal.key);
774 silc_client_ftp_session_free_client(conn, client_entry);
775 if (client_entry->internal->ke)
776 silc_client_abort_key_agreement(client, conn, client_entry);
778 silc_free(client_entry);
781 /* Removes client from the cache by the client entry. */
783 SilcBool silc_client_del_client(SilcClient client, SilcClientConnection conn,
784 SilcClientEntry client_entry)
786 SilcBool ret = silc_idcache_del_by_context(conn->internal->client_cache,
790 /* Remove from channels */
791 silc_client_remove_from_channels(client, conn, client_entry);
793 /* Free the client entry data */
794 silc_client_del_client_entry(client, conn, client_entry);
801 /* Take reference of client entry */
803 void silc_client_ref_client(SilcClient client, SilcClientConnection conn,
804 SilcClientEntry client_entry)
806 silc_atomic_add_int8(&client_entry->internal.refcnt, 1);
809 /* Release reference of client entry */
811 void silc_client_unref_client(SilcClient client, SilcClientConnection conn,
812 SilcClientEntry client_entry)
815 silc_atomic_sub_int8(&client_entry->internal.refcnt, 1) == 0)
816 silc_client_del_client(client, conn, client_entry);
819 /* Free client entry list */
821 void silc_client_list_free(SilcClient client, SilcClientConnection conn,
822 SilcDList client_list)
824 SilcClientEntry client_entry;
827 silc_dlist_start(client_list);
828 while ((client_entry = silc_dlist_get(client_list)))
829 silc_client_unref_client(client, conn, client_entry);
831 silc_dlist_uninit(client_list);
836 /************************* Channel Entry Routines ***************************/
838 /* Add new channel entry to the ID Cache */
840 SilcChannelEntry silc_client_add_channel(SilcClient client,
841 SilcClientConnection conn,
842 const char *channel_name,
844 SilcChannelID *channel_id)
846 SilcChannelEntry channel;
849 SILC_LOG_DEBUG(("Start"));
851 channel = silc_calloc(1, sizeof(*channel));
852 channel->channel_name = strdup(channel_name);
853 channel->id = channel_id;
854 channel->mode = mode;
855 channel->user_list = silc_hash_table_alloc(1, silc_hash_ptr, NULL, NULL,
856 NULL, NULL, NULL, TRUE);
858 /* Normalize channel name */
859 channel_namec = silc_channel_name_check(channel_name, strlen(channel_name),
860 SILC_STRING_UTF8, 256, NULL);
861 if (!channel_namec) {
862 silc_free(channel->channel_name);
863 silc_hash_table_free(channel->user_list);
868 /* Put it to the ID cache */
869 if (!silc_idcache_add(conn->internal->channel_cache, channel_namec,
870 (void *)channel->id, (void *)channel)) {
871 silc_free(channel_namec);
872 silc_free(channel->channel_name);
873 silc_hash_table_free(channel->user_list);
881 /* Foreach callbcak to free all users from the channel when deleting a
884 static void silc_client_del_channel_foreach(void *key, void *context,
887 SilcChannelUser chu = (SilcChannelUser)context;
889 SILC_LOG_DEBUG(("Start"));
891 /* Remove the context from the client's channel hash table as that
892 table and channel's user_list hash table share this same context. */
893 silc_hash_table_del(chu->client->channels, chu->channel);
897 /* Removes channel from the cache by the channel entry. */
899 SilcBool silc_client_del_channel(SilcClient client, SilcClientConnection conn,
900 SilcChannelEntry channel)
902 SilcBool ret = silc_idcache_del_by_context(conn->internal->channel_cache,
905 SILC_LOG_DEBUG(("Start"));
907 /* Free all client entrys from the users list. The silc_hash_table_free
908 will free all the entries so they are not freed at the foreach
910 silc_hash_table_foreach(channel->user_list, silc_client_del_channel_foreach,
912 silc_hash_table_free(channel->user_list);
914 silc_free(channel->channel_name);
915 silc_free(channel->topic);
916 silc_free(channel->id);
917 if (channel->founder_key)
918 silc_pkcs_public_key_free(channel->founder_key);
919 silc_free(channel->key);
920 if (channel->channel_key)
921 silc_cipher_free(channel->channel_key);
923 silc_hmac_free(channel->hmac);
924 if (channel->old_channel_keys) {
926 silc_dlist_start(channel->old_channel_keys);
927 while ((key = silc_dlist_get(channel->old_channel_keys)) != SILC_LIST_END)
928 silc_cipher_free(key);
929 silc_dlist_uninit(channel->old_channel_keys);
931 if (channel->old_hmacs) {
933 silc_dlist_start(channel->old_hmacs);
934 while ((hmac = silc_dlist_get(channel->old_hmacs)) != SILC_LIST_END)
935 silc_hmac_free(hmac);
936 silc_dlist_uninit(channel->old_hmacs);
938 silc_schedule_task_del_by_context(conn->client->schedule, channel);
939 silc_client_del_channel_private_keys(client, conn, channel);
944 /* Replaces the channel ID of the `channel' to `new_id'. Returns FALSE
945 if the ID could not be changed. */
947 SilcBool silc_client_replace_channel_id(SilcClient client,
948 SilcClientConnection conn,
949 SilcChannelEntry channel,
950 SilcChannelID *new_id)
957 SILC_LOG_DEBUG(("Old Channel ID id(%s)",
958 silc_id_render(channel->id, SILC_ID_CHANNEL)));
959 SILC_LOG_DEBUG(("New Channel ID id(%s)",
960 silc_id_render(new_id, SILC_ID_CHANNEL)));
962 silc_idcache_del_by_id(conn->internal->channel_cache, channel->id, NULL);
963 silc_free(channel->id);
964 channel->id = new_id;
966 /* Normalize channel name */
967 channel_namec = silc_channel_name_check(channel->channel_name,
968 strlen(channel->channel_name),
969 SILC_STRING_UTF8, 256, NULL);
973 return silc_idcache_add(conn->internal->channel_cache, channel_namec,
974 channel->id, channel) != NULL;
977 /* Finds entry for channel by the channel name. Returns the entry or NULL
978 if the entry was not found. It is found only if the client is joined
981 SilcChannelEntry silc_client_get_channel(SilcClient client,
982 SilcClientConnection conn,
985 SilcIDCacheEntry id_cache;
986 SilcChannelEntry entry;
988 assert(client && conn);
992 SILC_LOG_DEBUG(("Start"));
994 /* Normalize name for search */
995 channel = silc_channel_name_check(channel, strlen(channel), SILC_STRING_UTF8,
1000 if (!silc_idcache_find_by_name_one(conn->internal->channel_cache, channel,
1006 entry = (SilcChannelEntry)id_cache->context;
1008 SILC_LOG_DEBUG(("Found"));
1015 /* Finds entry for channel by the channel ID. Returns the entry or NULL
1016 if the entry was not found. It is found only if the client is joined
1019 SilcChannelEntry silc_client_get_channel_by_id(SilcClient client,
1020 SilcClientConnection conn,
1021 SilcChannelID *channel_id)
1023 SilcIDCacheEntry id_cache;
1024 SilcChannelEntry entry;
1026 assert(client && conn);
1030 SILC_LOG_DEBUG(("Start"));
1032 if (!silc_idcache_find_by_id_one(conn->internal->channel_cache, channel_id,
1036 entry = (SilcChannelEntry)id_cache->context;
1038 SILC_LOG_DEBUG(("Found"));
1046 SilcClientConnection conn;
1048 SilcChannelID *channel_id;
1051 SilcGetChannelCallback completion;
1053 } *GetChannelInternal;
1055 SILC_CLIENT_CMD_FUNC(get_channel_resolve_callback)
1057 GetChannelInternal i = (GetChannelInternal)context;
1058 SilcChannelEntry entry;
1060 SILC_LOG_DEBUG(("Start"));
1062 /* Get the channel */
1063 entry = silc_client_get_channel(i->client, i->conn, i->u.channel_name);
1065 i->completion(i->client, i->conn, &entry, 1, i->context);
1067 i->completion(i->client, i->conn, NULL, 0, i->context);
1070 silc_free(i->u.channel_name);
1074 /* Resolves channel entry from the server by the channel name. */
1076 void silc_client_get_channel_resolve(SilcClient client,
1077 SilcClientConnection conn,
1079 SilcGetChannelCallback completion,
1082 GetChannelInternal i = silc_calloc(1, sizeof(*i));
1084 assert(client && conn && channel_name);
1086 SILC_LOG_DEBUG(("Start"));
1090 i->u.channel_name = strdup(channel_name);
1091 i->completion = completion;
1092 i->context = context;
1094 /* Register our own command reply for this command */
1095 silc_client_command_register(client, SILC_COMMAND_IDENTIFY, NULL, NULL,
1096 silc_client_command_reply_identify_i, 0,
1099 /* Send the command */
1100 silc_client_command_send(client, conn, SILC_COMMAND_IDENTIFY,
1102 1, 3, channel_name, strlen(channel_name));
1104 /* Add pending callback */
1105 silc_client_command_pending(conn, SILC_COMMAND_IDENTIFY, conn->cmd_ident,
1106 silc_client_command_get_channel_resolve_callback,
1110 SILC_CLIENT_CMD_FUNC(get_channel_by_id_callback)
1112 GetChannelInternal i = (GetChannelInternal)context;
1113 SilcChannelEntry entry;
1115 SILC_LOG_DEBUG(("Start"));
1117 /* Get the channel */
1118 entry = silc_client_get_channel_by_id(i->client, i->conn, i->u.channel_id);
1120 i->completion(i->client, i->conn, &entry, 1, i->context);
1122 i->completion(i->client, i->conn, NULL, 0, i->context);
1125 silc_free(i->u.channel_id);
1129 /* Resolves channel information from the server by the channel ID. */
1131 void silc_client_get_channel_by_id_resolve(SilcClient client,
1132 SilcClientConnection conn,
1133 SilcChannelID *channel_id,
1134 SilcGetChannelCallback completion,
1138 GetChannelInternal i = silc_calloc(1, sizeof(*i));
1140 assert(client && conn && channel_id);
1142 SILC_LOG_DEBUG(("Start"));
1146 i->u.channel_id = silc_id_dup(channel_id, SILC_ID_CHANNEL);
1147 i->completion = completion;
1148 i->context = context;
1150 /* Register our own command reply for this command */
1151 silc_client_command_register(client, SILC_COMMAND_IDENTIFY, NULL, NULL,
1152 silc_client_command_reply_identify_i, 0,
1155 /* Send the command */
1156 idp = silc_id_payload_encode(channel_id, SILC_ID_CHANNEL);
1157 silc_client_command_send(client, conn, SILC_COMMAND_IDENTIFY,
1159 1, 5, idp->data, idp->len);
1160 silc_buffer_free(idp);
1162 /* Add pending callback */
1163 silc_client_command_pending(conn, SILC_COMMAND_IDENTIFY, conn->cmd_ident,
1164 silc_client_command_get_channel_by_id_callback,
1169 /* Finds entry for server by the server name. */
1171 SilcServerEntry silc_client_get_server(SilcClient client,
1172 SilcClientConnection conn,
1175 SilcIDCacheEntry id_cache;
1176 SilcServerEntry entry;
1178 assert(client && conn);
1182 SILC_LOG_DEBUG(("Start"));
1184 /* Normalize server name for search */
1185 server_name = silc_identifier_check(server_name, strlen(server_name),
1186 SILC_STRING_UTF8, 256, NULL);
1190 if (!silc_idcache_find_by_name_one(conn->internal->server_cache,
1191 server_name, &id_cache)) {
1192 silc_free(server_name);
1196 entry = (SilcServerEntry)id_cache->context;
1198 silc_free(server_name);
1203 /* Finds entry for server by the server ID. */
1205 SilcServerEntry silc_client_get_server_by_id(SilcClient client,
1206 SilcClientConnection conn,
1207 SilcServerID *server_id)
1209 SilcIDCacheEntry id_cache;
1210 SilcServerEntry entry;
1212 assert(client && conn);
1216 SILC_LOG_DEBUG(("Start"));
1218 if (!silc_idcache_find_by_id_one(conn->internal->server_cache,
1219 (void *)server_id, &id_cache))
1222 entry = (SilcServerEntry)id_cache->context;
1227 /* Add new server entry */
1229 SilcServerEntry silc_client_add_server(SilcClient client,
1230 SilcClientConnection conn,
1231 const char *server_name,
1232 const char *server_info,
1233 SilcServerID *server_id)
1235 SilcServerEntry server_entry;
1236 char *server_namec = NULL;
1238 SILC_LOG_DEBUG(("Start"));
1240 server_entry = silc_calloc(1, sizeof(*server_entry));
1241 if (!server_entry || !server_id)
1244 server_entry->server_id = server_id;
1246 server_entry->server_name = strdup(server_name);
1248 server_entry->server_info = strdup(server_info);
1250 /* Normalize server name */
1252 server_namec = silc_identifier_check(server_name, strlen(server_name),
1253 SILC_STRING_UTF8, 256, NULL);
1254 if (!server_namec) {
1255 silc_free(server_entry->server_id);
1256 silc_free(server_entry->server_name);
1257 silc_free(server_entry->server_info);
1258 silc_free(server_entry);
1263 /* Add server to cache */
1264 if (!silc_idcache_add(conn->internal->server_cache, server_namec,
1265 server_entry->server_id, server_entry)) {
1266 silc_free(server_namec);
1267 silc_free(server_entry->server_id);
1268 silc_free(server_entry->server_name);
1269 silc_free(server_entry->server_info);
1270 silc_free(server_entry);
1274 return server_entry;
1277 /* Removes server from the cache by the server entry. */
1279 SilcBool silc_client_del_server(SilcClient client, SilcClientConnection conn,
1280 SilcServerEntry server)
1282 SilcBool ret = silc_idcache_del_by_context(conn->internal->server_cache,
1284 silc_free(server->server_name);
1285 silc_free(server->server_info);
1286 silc_free(server->server_id);
1291 /* Updates the `server_entry' with the new information sent as argument. */
1293 void silc_client_update_server(SilcClient client,
1294 SilcClientConnection conn,
1295 SilcServerEntry server_entry,
1296 const char *server_name,
1297 const char *server_info)
1299 char *server_namec = NULL;
1301 SILC_LOG_DEBUG(("Start"));
1304 (!server_entry->server_name ||
1305 !silc_utf8_strcasecmp(server_entry->server_name, server_name))) {
1307 silc_idcache_del_by_context(conn->internal->server_cache,
1308 server_entry, NULL);
1309 silc_free(server_entry->server_name);
1310 server_entry->server_name = strdup(server_name);
1312 /* Normalize server name */
1314 server_namec = silc_identifier_check(server_name, strlen(server_name),
1315 SILC_STRING_UTF8, 256, NULL);
1319 silc_idcache_add(conn->internal->server_cache, server_namec,
1320 server_entry->server_id, server_entry);
1325 (!server_entry->server_info ||
1326 !silc_utf8_strcasecmp(server_entry->server_info, server_info))) {
1327 silc_free(server_entry->server_info);
1328 server_entry->server_info = strdup(server_info);
1332 /* Formats the nickname of the client specified by the `client_entry'.
1333 If the format is specified by the application this will format the
1334 nickname and replace the old nickname in the client entry. If the
1335 format string is not specified then this function has no effect. */
1337 void silc_client_nickname_format(SilcClient client,
1338 SilcClientConnection conn,
1339 SilcClientEntry client_entry)
1342 char newnick[128 + 1];
1343 int i, off = 0, len;
1346 SilcClientEntry entry, unformatted = NULL;
1348 SILC_LOG_DEBUG(("Start"));
1350 if (!client->internal->params->nickname_format[0])
1353 if (!client_entry->nickname[0])
1356 /* Get all clients with same nickname. Do not perform the formatting
1357 if there aren't any clients with same nickname unless the application
1358 is forcing us to do so. */
1359 clients = silc_client_get_clients_local(client, conn,
1360 client_entry->nickname, NULL);
1361 if (!clients && !client->internal->params->nickname_force_format)
1366 while ((entry = silc_dlist_get(clients))) {
1367 if (entry->internal.valid && entry != client_entry)
1369 if (entry->internal.valid && entry != client_entry &&
1370 silc_utf8_strcasecmp(entry->nickname, client_entry->nickname)) {
1372 unformatted = entry;
1375 if (!len || freebase)
1378 /* If we are changing nickname of our local entry we'll enforce
1379 that we will always get the unformatted nickname. Give our
1380 format number to the one that is not formatted now. */
1381 if (unformatted && client_entry == conn->local_entry)
1382 client_entry = unformatted;
1384 memset(newnick, 0, sizeof(newnick));
1385 cp = client->internal->params->nickname_format;
1395 if (!client_entry->nickname[0])
1397 len = strlen(client_entry->nickname);
1398 memcpy(&newnick[off], client_entry->nickname, len);
1402 /* Stripped hostname */
1403 if (!client_entry->hostname[0])
1405 len = strcspn(client_entry->hostname, ".");
1406 i = strcspn(client_entry->hostname, "-");
1409 memcpy(&newnick[off], client_entry->hostname, len);
1414 if (!client_entry->hostname[0])
1416 len = strlen(client_entry->hostname);
1417 memcpy(&newnick[off], client_entry->hostname, len);
1421 /* Stripped server name */
1422 if (!client_entry->server)
1424 len = strcspn(client_entry->server, ".");
1425 memcpy(&newnick[off], client_entry->server, len);
1429 /* Full server name */
1430 if (!client_entry->server)
1432 len = strlen(client_entry->server);
1433 memcpy(&newnick[off], client_entry->server, len);
1437 /* Ascending number */
1442 if (silc_dlist_count(clients) == 1)
1445 silc_dlist_start(clients);
1446 while ((entry = silc_dlist_get(clients))) {
1447 if (!silc_utf8_strncasecmp(entry->nickname, newnick, off))
1449 if (strlen(entry->nickname) <= off)
1451 num = atoi(&entry->nickname[off]);
1456 memset(tmp, 0, sizeof(tmp));
1457 snprintf(tmp, sizeof(tmp) - 1, "%d", ++max);
1459 memcpy(&newnick[off], tmp, len);
1464 /* Some other character in the string */
1465 memcpy(&newnick[off], cp, 1);
1474 memcpy(client_entry->nickname, newnick, strlen(newnick));
1475 silc_client_list_free(client, conn, clients);