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_client_ref_client(client, conn, client_entry);
350 silc_dlist_add(clients, client_entry);
353 if (!silc_buffer_pull(i->client_id_list, idp_len)) {
354 status = SILC_STATUS_ERR_BAD_CLIENT_ID;
359 silc_dlist_start(clients);
360 status = SILC_STATUS_OK;
362 i->completion(client, conn, status, clients, i->context);
365 if (status != SILC_STATUS_OK && i->completion)
366 i->completion(client, conn, status, NULL, i->context);
367 silc_client_list_free(client, conn, clients);
372 /* Gets client entries by the list of client ID's `client_id_list'. This
373 always resolves those client ID's it does not know yet from the server
374 so this function might take a while. The `client_id_list' is a list
375 of ID Payloads added one after other. JOIN command reply and USERS
376 command reply for example returns this sort of list. The `completion'
377 will be called after the entries are available. */
379 void silc_client_get_clients_by_list(SilcClient client,
380 SilcClientConnection conn,
381 SilcUInt32 list_count,
382 SilcBuffer client_id_list,
383 SilcGetClientCallback completion,
386 GetClientsByListInternal in;
387 SilcClientEntry entry;
388 unsigned char **res_argv = NULL;
389 SilcUInt32 *res_argv_lens = NULL, *res_argv_types = NULL, res_argc = 0;
394 SILC_LOG_DEBUG(("Resolve clients from Client ID list"));
396 if (!client || !conn || !client_id_list)
399 in = silc_calloc(1, sizeof(*in));
402 in->completion = completion;
403 in->context = context;
404 in->list_count = list_count;
405 in->client_id_list = silc_buffer_copy(client_id_list);
406 if (!in->client_id_list)
409 for (i = 0; i < list_count; i++) {
411 SILC_GET16_MSB(idp_len, client_id_list->data + 2);
413 if (!silc_id_payload_parse_id(client_id_list->data, idp_len, &id))
416 /* Check if we have this client cached already. If we don't have the
417 entry or it has incomplete info, then resolve it from the server. */
418 entry = silc_client_get_client_by_id(client, conn, &id.u.client_id);
419 if (!entry || !entry->nickname || !entry->username || !entry->realname) {
421 res_argv = silc_calloc(list_count, sizeof(*res_argv));
422 res_argv_lens = silc_calloc(list_count, sizeof(*res_argv_lens));
423 res_argv_types = silc_calloc(list_count, sizeof(*res_argv_types));
424 if (!res_argv || !res_argv_lens || !res_argv_types)
428 res_argv[res_argc] = client_id_list->data;
429 res_argv_lens[res_argc] = idp_len;
430 res_argv_types[res_argc] = res_argc + 5;
434 if (!silc_buffer_pull(client_id_list, idp_len))
437 silc_buffer_start(client_id_list);
439 /* Query the unknown client information from server */
441 silc_client_command_send_argv(client, conn, SILC_COMMAND_WHOIS,
442 silc_client_get_clients_list_cb,
443 in, res_argc, res_argv, res_argv_lens,
446 silc_free(res_argv_lens);
447 silc_free(res_argv_types);
451 /* We have the clients in cache, get them and call the completion */
452 silc_client_get_clients_list_cb(client, conn, SILC_COMMAND_WHOIS,
453 SILC_STATUS_OK, SILC_STATUS_OK, in, NULL);
457 silc_buffer_free(in->client_id_list);
460 silc_free(res_argv_lens);
461 silc_free(res_argv_types);
467 SilcClientConnection conn;
468 SilcChannelID channel_id;
469 SilcGetClientCallback completion;
472 } *GetClientsByChannelInternal;
474 SILC_CLIENT_CMD_FUNC(get_clients_by_channel_cb)
476 GetClientsByChannelInternal i = context;
477 SilcClientEntry *clients = NULL;
478 SilcUInt32 clients_count = 0;
479 SilcBool found = FALSE;
480 SilcChannelEntry channel;
481 SilcHashTableList htl;
490 channel = silc_client_get_channel_by_id(i->client, i->conn, &i->channel_id);
491 if (channel && !silc_hash_table_count(channel->user_list)) {
492 clients = silc_calloc(silc_hash_table_count(channel->user_list),
494 silc_hash_table_list(channel->user_list, &htl);
495 while (silc_hash_table_get(&htl, NULL, (void *)&chu))
496 clients[clients_count++] = chu->client;
497 silc_hash_table_list_reset(&htl);
502 i->completion(i->client, i->conn, clients, clients_count, i->context);
505 i->completion(i->client, i->conn, NULL, 0, i->context);
511 /* Gets client entries by the channel entry indicated by `channel'. Thus,
512 it resolves the clients currently on that channel. */
514 void silc_client_get_clients_by_channel(SilcClient client,
515 SilcClientConnection conn,
516 SilcChannelEntry channel,
517 SilcGetClientCallback completion,
520 GetClientsByChannelInternal in;
521 SilcHashTableList htl;
523 SilcClientEntry entry;
524 unsigned char **res_argv = NULL;
525 SilcUInt32 *res_argv_lens = NULL, *res_argv_types = NULL, res_argc = 0;
527 SilcBool wait_res = FALSE;
529 assert(client && conn && channel);
531 SILC_LOG_DEBUG(("Start"));
533 in = silc_calloc(1, sizeof(*in));
536 in->channel_id = *channel->id;
537 in->completion = completion;
538 in->context = context;
540 /* If user list does not exist, send USERS command. */
541 if (!channel->user_list || !silc_hash_table_count(channel->user_list)) {
542 SILC_LOG_DEBUG(("Sending USERS"));
543 silc_client_command_register(client, SILC_COMMAND_USERS, NULL, NULL,
544 silc_client_command_reply_users_i, 0,
546 silc_client_command_send(client, conn, SILC_COMMAND_USERS,
547 conn->cmd_ident, 1, 2, channel->channel_name,
548 strlen(channel->channel_name));
549 silc_client_command_pending(conn, SILC_COMMAND_USERS, conn->cmd_ident,
550 silc_client_command_get_clients_by_channel_cb,
555 silc_hash_table_list(channel->user_list, &htl);
556 while (silc_hash_table_get(&htl, NULL, (void *)&chu)) {
559 /* If the entry has incomplete info, then resolve it from the server. */
560 if (!entry->nickname || !entry->realname) {
561 if (entry->status & SILC_CLIENT_STATUS_RESOLVING) {
562 /* Attach to this resolving and wait until it finishes */
563 silc_client_command_pending(
564 conn, SILC_COMMAND_NONE,
565 entry->resolve_cmd_ident,
566 silc_client_command_get_clients_by_channel_cb,
572 entry->status |= SILC_CLIENT_STATUS_RESOLVING;
573 entry->resolve_cmd_ident = conn->cmd_ident + 1;
575 idp = silc_id_payload_encode(entry->id, SILC_ID_CLIENT);
577 /* No we don't have it, query it from the server. Assemble argument
578 table that will be sent for the WHOIS command later. */
579 res_argv = silc_realloc(res_argv, sizeof(*res_argv) *
581 res_argv_lens = silc_realloc(res_argv_lens, sizeof(*res_argv_lens) *
583 res_argv_types = silc_realloc(res_argv_types, sizeof(*res_argv_types) *
585 res_argv[res_argc] = silc_memdup(idp->data, idp->len);
586 res_argv_lens[res_argc] = idp->len;
587 res_argv_types[res_argc] = res_argc + 4;
590 silc_buffer_free(idp);
593 silc_hash_table_list_reset(&htl);
595 /* Query the client information from server if the list included clients
596 that we don't know about. */
600 /* Send the WHOIS command to server */
601 res_cmd = silc_command_payload_encode(SILC_COMMAND_WHOIS,
602 res_argc, res_argv, res_argv_lens,
603 res_argv_types, ++conn->cmd_ident);
604 silc_client_packet_send(client, conn->sock, SILC_PACKET_COMMAND,
605 NULL, 0, NULL, NULL, res_cmd->data, res_cmd->len,
608 /* Register our own command reply for this command */
609 silc_client_command_register(client, SILC_COMMAND_WHOIS, NULL, NULL,
610 silc_client_command_reply_whois_i, 0,
613 /* Process the applications request after reply has been received */
614 silc_client_command_pending(
615 conn, SILC_COMMAND_WHOIS, conn->cmd_ident,
616 silc_client_command_get_clients_by_channel_cb,
620 silc_buffer_free(res_cmd);
622 silc_free(res_argv_lens);
623 silc_free(res_argv_types);
630 /* We have the clients in cache, get them and call the completion */
631 silc_client_command_get_clients_by_channel_cb((void *)in, NULL);
636 /************************** Client Entry Routines ***************************/
638 /* Creates new client entry and adds it to the ID cache. Returns pointer
641 SilcClientEntry silc_client_add_client(SilcClient client,
642 SilcClientConnection conn,
643 char *nickname, char *username,
644 char *userinfo, SilcClientID *id,
647 SilcClientEntry client_entry;
650 SILC_LOG_DEBUG(("Adding new client entry"));
652 /* Save the client infos */
653 client_entry = silc_calloc(1, sizeof(*client_entry));
657 client_entry->id = *id;
658 client_entry->internal.valid = TRUE;
659 client_entry->mode = mode;
660 client_entry->realname = userinfo ? strdup(userinfo) : NULL;
661 silc_parse_userfqdn(nickname, client_entry->nickname,
662 sizeof(client_entry->nickname),
663 client_entry->server,
664 sizeof(client_entry->server));
665 silc_parse_userfqdn(username, client_entry->username,
666 sizeof(client_entry->username),
667 client_entry->hostname,
668 sizeof(client_entry->hostname));
669 client_entry->channels = silc_hash_table_alloc(1, silc_hash_ptr, NULL, NULL,
670 NULL, NULL, NULL, TRUE);
671 if (!client_entry->channels) {
672 silc_free(client_entry->realname);
673 silc_free(client_entry);
677 /* Normalize nickname */
678 if (client_entry->nickname[0]) {
679 nick = silc_identifier_check(client_entry->nickname,
680 strlen(client_entry->nickname),
681 SILC_STRING_UTF8, 128, NULL);
683 silc_free(client_entry->realname);
684 silc_hash_table_free(client_entry->channels);
685 silc_free(client_entry);
690 /* Format the nickname */
691 silc_client_nickname_format(client, conn, client_entry);
693 /* Add client to cache, the normalized nickname is saved to cache */
694 if (!silc_idcache_add(conn->internal->client_cache, nick,
695 &client_entry->id, client_entry)) {
697 silc_free(client_entry->realname);
698 silc_hash_table_free(client_entry->channels);
699 silc_free(client_entry);
703 client_entry->nickname_normalized = nick;
708 /* Updates the `client_entry' with the new information sent as argument. */
710 void silc_client_update_client(SilcClient client,
711 SilcClientConnection conn,
712 SilcClientEntry client_entry,
713 const char *nickname,
714 const char *username,
715 const char *userinfo,
720 SILC_LOG_DEBUG(("Update client entry"));
722 if (!client_entry->realname && userinfo)
723 client_entry->realname = strdup(userinfo);
724 if ((!client_entry->username[0] || !client_entry->hostname[0]) && username)
725 silc_parse_userfqdn(username, client_entry->username,
726 sizeof(client_entry->username),
727 client_entry->hostname,
728 sizeof(client_entry->username));
729 if (!client_entry->nickname[0] && nickname) {
730 silc_parse_userfqdn(nickname, client_entry->nickname,
731 sizeof(client_entry->nickname),
732 client_entry->server,
733 sizeof(client_entry->server));
735 /* Normalize nickname */
736 nick = silc_identifier_check(client_entry->nickname,
737 strlen(client_entry->nickname),
738 SILC_STRING_UTF8, 128, NULL);
742 /* Format nickname */
743 silc_client_nickname_format(client, conn, client_entry);
745 /* Remove the old cache entry and create a new one */
746 silc_idcache_del_by_context(conn->internal->client_cache, client_entry,
748 silc_idcache_add(conn->internal->client_cache, nick, &client_entry->id,
751 client_entry->mode = mode;
754 /* Deletes the client entry and frees all memory. */
756 void silc_client_del_client_entry(SilcClient client,
757 SilcClientConnection conn,
758 SilcClientEntry client_entry)
760 SILC_LOG_DEBUG(("Start"));
762 silc_free(client_entry->realname);
763 if (client_entry->public_key)
764 silc_pkcs_public_key_free(client_entry->public_key);
765 silc_hash_table_free(client_entry->channels);
766 if (client_entry->internal.send_key)
767 silc_cipher_free(client_entry->internal.send_key);
768 if (client_entry->internal.receive_key)
769 silc_cipher_free(client_entry->internal.receive_key);
770 silc_free(client_entry->internal.key);
772 silc_client_ftp_session_free_client(conn, client_entry);
773 if (client_entry->internal->ke)
774 silc_client_abort_key_agreement(client, conn, client_entry);
776 silc_free(client_entry);
779 /* Removes client from the cache by the client entry. */
781 SilcBool silc_client_del_client(SilcClient client, SilcClientConnection conn,
782 SilcClientEntry client_entry)
784 SilcBool ret = silc_idcache_del_by_context(conn->internal->client_cache,
788 /* Remove from channels */
789 silc_client_remove_from_channels(client, conn, client_entry);
791 /* Free the client entry data */
792 silc_client_del_client_entry(client, conn, client_entry);
799 /* Take reference of client entry */
801 void silc_client_ref_client(SilcClient client, SilcClientConnection conn,
802 SilcClientEntry client_entry)
804 silc_atomic_add_int8(&client_entry->internal.refcnt, 1);
807 /* Release reference of client entry */
809 void silc_client_unref_client(SilcClient client, SilcClientConnection conn,
810 SilcClientEntry client_entry)
812 if (silc_atomic_sub_int8(&client_entry->internal.refcnt, 1) == 0) {
813 silc_client_del_client(client, conn, client_entry);
818 /* Free client entry list */
820 void silc_client_list_free(SilcClient client, SilcClientConnection conn,
821 SilcDList client_list)
823 SilcClientEntry client_entry;
825 silc_dlist_start(client_list);
826 while ((client_entry = silc_dlist_get(client_list)))
827 silc_client_unref_client(client, conn, client_entry);
829 silc_dlist_uninit(client_list);
833 /************************* Channel Entry Routines ***************************/
835 /* Add new channel entry to the ID Cache */
837 SilcChannelEntry silc_client_add_channel(SilcClient client,
838 SilcClientConnection conn,
839 const char *channel_name,
841 SilcChannelID *channel_id)
843 SilcChannelEntry channel;
846 SILC_LOG_DEBUG(("Start"));
848 channel = silc_calloc(1, sizeof(*channel));
849 channel->channel_name = strdup(channel_name);
850 channel->id = channel_id;
851 channel->mode = mode;
852 channel->user_list = silc_hash_table_alloc(1, silc_hash_ptr, NULL, NULL,
853 NULL, NULL, NULL, TRUE);
855 /* Normalize channel name */
856 channel_namec = silc_channel_name_check(channel_name, strlen(channel_name),
857 SILC_STRING_UTF8, 256, NULL);
858 if (!channel_namec) {
859 silc_free(channel->channel_name);
860 silc_hash_table_free(channel->user_list);
865 /* Put it to the ID cache */
866 if (!silc_idcache_add(conn->internal->channel_cache, channel_namec,
867 (void *)channel->id, (void *)channel)) {
868 silc_free(channel_namec);
869 silc_free(channel->channel_name);
870 silc_hash_table_free(channel->user_list);
878 /* Foreach callbcak to free all users from the channel when deleting a
881 static void silc_client_del_channel_foreach(void *key, void *context,
884 SilcChannelUser chu = (SilcChannelUser)context;
886 SILC_LOG_DEBUG(("Start"));
888 /* Remove the context from the client's channel hash table as that
889 table and channel's user_list hash table share this same context. */
890 silc_hash_table_del(chu->client->channels, chu->channel);
894 /* Removes channel from the cache by the channel entry. */
896 SilcBool silc_client_del_channel(SilcClient client, SilcClientConnection conn,
897 SilcChannelEntry channel)
899 SilcBool ret = silc_idcache_del_by_context(conn->internal->channel_cache,
902 SILC_LOG_DEBUG(("Start"));
904 /* Free all client entrys from the users list. The silc_hash_table_free
905 will free all the entries so they are not freed at the foreach
907 silc_hash_table_foreach(channel->user_list, silc_client_del_channel_foreach,
909 silc_hash_table_free(channel->user_list);
911 silc_free(channel->channel_name);
912 silc_free(channel->topic);
913 silc_free(channel->id);
914 if (channel->founder_key)
915 silc_pkcs_public_key_free(channel->founder_key);
916 silc_free(channel->key);
917 if (channel->channel_key)
918 silc_cipher_free(channel->channel_key);
920 silc_hmac_free(channel->hmac);
921 if (channel->old_channel_keys) {
923 silc_dlist_start(channel->old_channel_keys);
924 while ((key = silc_dlist_get(channel->old_channel_keys)) != SILC_LIST_END)
925 silc_cipher_free(key);
926 silc_dlist_uninit(channel->old_channel_keys);
928 if (channel->old_hmacs) {
930 silc_dlist_start(channel->old_hmacs);
931 while ((hmac = silc_dlist_get(channel->old_hmacs)) != SILC_LIST_END)
932 silc_hmac_free(hmac);
933 silc_dlist_uninit(channel->old_hmacs);
935 silc_schedule_task_del_by_context(conn->client->schedule, channel);
936 silc_client_del_channel_private_keys(client, conn, channel);
941 /* Replaces the channel ID of the `channel' to `new_id'. Returns FALSE
942 if the ID could not be changed. */
944 SilcBool silc_client_replace_channel_id(SilcClient client,
945 SilcClientConnection conn,
946 SilcChannelEntry channel,
947 SilcChannelID *new_id)
954 SILC_LOG_DEBUG(("Old Channel ID id(%s)",
955 silc_id_render(channel->id, SILC_ID_CHANNEL)));
956 SILC_LOG_DEBUG(("New Channel ID id(%s)",
957 silc_id_render(new_id, SILC_ID_CHANNEL)));
959 silc_idcache_del_by_id(conn->internal->channel_cache, channel->id, NULL);
960 silc_free(channel->id);
961 channel->id = new_id;
963 /* Normalize channel name */
964 channel_namec = silc_channel_name_check(channel->channel_name,
965 strlen(channel->channel_name),
966 SILC_STRING_UTF8, 256, NULL);
970 return silc_idcache_add(conn->internal->channel_cache, channel_namec,
971 channel->id, channel) != NULL;
974 /* Finds entry for channel by the channel name. Returns the entry or NULL
975 if the entry was not found. It is found only if the client is joined
978 SilcChannelEntry silc_client_get_channel(SilcClient client,
979 SilcClientConnection conn,
982 SilcIDCacheEntry id_cache;
983 SilcChannelEntry entry;
985 assert(client && conn);
989 SILC_LOG_DEBUG(("Start"));
991 /* Normalize name for search */
992 channel = silc_channel_name_check(channel, strlen(channel), SILC_STRING_UTF8,
997 if (!silc_idcache_find_by_name_one(conn->internal->channel_cache, channel,
1003 entry = (SilcChannelEntry)id_cache->context;
1005 SILC_LOG_DEBUG(("Found"));
1012 /* Finds entry for channel by the channel ID. Returns the entry or NULL
1013 if the entry was not found. It is found only if the client is joined
1016 SilcChannelEntry silc_client_get_channel_by_id(SilcClient client,
1017 SilcClientConnection conn,
1018 SilcChannelID *channel_id)
1020 SilcIDCacheEntry id_cache;
1021 SilcChannelEntry entry;
1023 assert(client && conn);
1027 SILC_LOG_DEBUG(("Start"));
1029 if (!silc_idcache_find_by_id_one(conn->internal->channel_cache, channel_id,
1033 entry = (SilcChannelEntry)id_cache->context;
1035 SILC_LOG_DEBUG(("Found"));
1043 SilcClientConnection conn;
1045 SilcChannelID *channel_id;
1048 SilcGetChannelCallback completion;
1050 } *GetChannelInternal;
1052 SILC_CLIENT_CMD_FUNC(get_channel_resolve_callback)
1054 GetChannelInternal i = (GetChannelInternal)context;
1055 SilcChannelEntry entry;
1057 SILC_LOG_DEBUG(("Start"));
1059 /* Get the channel */
1060 entry = silc_client_get_channel(i->client, i->conn, i->u.channel_name);
1062 i->completion(i->client, i->conn, &entry, 1, i->context);
1064 i->completion(i->client, i->conn, NULL, 0, i->context);
1067 silc_free(i->u.channel_name);
1071 /* Resolves channel entry from the server by the channel name. */
1073 void silc_client_get_channel_resolve(SilcClient client,
1074 SilcClientConnection conn,
1076 SilcGetChannelCallback completion,
1079 GetChannelInternal i = silc_calloc(1, sizeof(*i));
1081 assert(client && conn && channel_name);
1083 SILC_LOG_DEBUG(("Start"));
1087 i->u.channel_name = strdup(channel_name);
1088 i->completion = completion;
1089 i->context = context;
1091 /* Register our own command reply for this command */
1092 silc_client_command_register(client, SILC_COMMAND_IDENTIFY, NULL, NULL,
1093 silc_client_command_reply_identify_i, 0,
1096 /* Send the command */
1097 silc_client_command_send(client, conn, SILC_COMMAND_IDENTIFY,
1099 1, 3, channel_name, strlen(channel_name));
1101 /* Add pending callback */
1102 silc_client_command_pending(conn, SILC_COMMAND_IDENTIFY, conn->cmd_ident,
1103 silc_client_command_get_channel_resolve_callback,
1107 SILC_CLIENT_CMD_FUNC(get_channel_by_id_callback)
1109 GetChannelInternal i = (GetChannelInternal)context;
1110 SilcChannelEntry entry;
1112 SILC_LOG_DEBUG(("Start"));
1114 /* Get the channel */
1115 entry = silc_client_get_channel_by_id(i->client, i->conn, i->u.channel_id);
1117 i->completion(i->client, i->conn, &entry, 1, i->context);
1119 i->completion(i->client, i->conn, NULL, 0, i->context);
1122 silc_free(i->u.channel_id);
1126 /* Resolves channel information from the server by the channel ID. */
1128 void silc_client_get_channel_by_id_resolve(SilcClient client,
1129 SilcClientConnection conn,
1130 SilcChannelID *channel_id,
1131 SilcGetChannelCallback completion,
1135 GetChannelInternal i = silc_calloc(1, sizeof(*i));
1137 assert(client && conn && channel_id);
1139 SILC_LOG_DEBUG(("Start"));
1143 i->u.channel_id = silc_id_dup(channel_id, SILC_ID_CHANNEL);
1144 i->completion = completion;
1145 i->context = context;
1147 /* Register our own command reply for this command */
1148 silc_client_command_register(client, SILC_COMMAND_IDENTIFY, NULL, NULL,
1149 silc_client_command_reply_identify_i, 0,
1152 /* Send the command */
1153 idp = silc_id_payload_encode(channel_id, SILC_ID_CHANNEL);
1154 silc_client_command_send(client, conn, SILC_COMMAND_IDENTIFY,
1156 1, 5, idp->data, idp->len);
1157 silc_buffer_free(idp);
1159 /* Add pending callback */
1160 silc_client_command_pending(conn, SILC_COMMAND_IDENTIFY, conn->cmd_ident,
1161 silc_client_command_get_channel_by_id_callback,
1166 /* Finds entry for server by the server name. */
1168 SilcServerEntry silc_client_get_server(SilcClient client,
1169 SilcClientConnection conn,
1172 SilcIDCacheEntry id_cache;
1173 SilcServerEntry entry;
1175 assert(client && conn);
1179 SILC_LOG_DEBUG(("Start"));
1181 /* Normalize server name for search */
1182 server_name = silc_identifier_check(server_name, strlen(server_name),
1183 SILC_STRING_UTF8, 256, NULL);
1187 if (!silc_idcache_find_by_name_one(conn->internal->server_cache,
1188 server_name, &id_cache)) {
1189 silc_free(server_name);
1193 entry = (SilcServerEntry)id_cache->context;
1195 silc_free(server_name);
1200 /* Finds entry for server by the server ID. */
1202 SilcServerEntry silc_client_get_server_by_id(SilcClient client,
1203 SilcClientConnection conn,
1204 SilcServerID *server_id)
1206 SilcIDCacheEntry id_cache;
1207 SilcServerEntry entry;
1209 assert(client && conn);
1213 SILC_LOG_DEBUG(("Start"));
1215 if (!silc_idcache_find_by_id_one(conn->internal->server_cache,
1216 (void *)server_id, &id_cache))
1219 entry = (SilcServerEntry)id_cache->context;
1224 /* Add new server entry */
1226 SilcServerEntry silc_client_add_server(SilcClient client,
1227 SilcClientConnection conn,
1228 const char *server_name,
1229 const char *server_info,
1230 SilcServerID *server_id)
1232 SilcServerEntry server_entry;
1233 char *server_namec = NULL;
1235 SILC_LOG_DEBUG(("Start"));
1237 server_entry = silc_calloc(1, sizeof(*server_entry));
1238 if (!server_entry || !server_id)
1241 server_entry->server_id = server_id;
1243 server_entry->server_name = strdup(server_name);
1245 server_entry->server_info = strdup(server_info);
1247 /* Normalize server name */
1249 server_namec = silc_identifier_check(server_name, strlen(server_name),
1250 SILC_STRING_UTF8, 256, NULL);
1251 if (!server_namec) {
1252 silc_free(server_entry->server_id);
1253 silc_free(server_entry->server_name);
1254 silc_free(server_entry->server_info);
1255 silc_free(server_entry);
1260 /* Add server to cache */
1261 if (!silc_idcache_add(conn->internal->server_cache, server_namec,
1262 server_entry->server_id, server_entry)) {
1263 silc_free(server_namec);
1264 silc_free(server_entry->server_id);
1265 silc_free(server_entry->server_name);
1266 silc_free(server_entry->server_info);
1267 silc_free(server_entry);
1271 return server_entry;
1274 /* Removes server from the cache by the server entry. */
1276 SilcBool silc_client_del_server(SilcClient client, SilcClientConnection conn,
1277 SilcServerEntry server)
1279 SilcBool ret = silc_idcache_del_by_context(conn->internal->server_cache,
1281 silc_free(server->server_name);
1282 silc_free(server->server_info);
1283 silc_free(server->server_id);
1288 /* Updates the `server_entry' with the new information sent as argument. */
1290 void silc_client_update_server(SilcClient client,
1291 SilcClientConnection conn,
1292 SilcServerEntry server_entry,
1293 const char *server_name,
1294 const char *server_info)
1296 char *server_namec = NULL;
1298 SILC_LOG_DEBUG(("Start"));
1301 (!server_entry->server_name ||
1302 !silc_utf8_strcasecmp(server_entry->server_name, server_name))) {
1304 silc_idcache_del_by_context(conn->internal->server_cache,
1305 server_entry, NULL);
1306 silc_free(server_entry->server_name);
1307 server_entry->server_name = strdup(server_name);
1309 /* Normalize server name */
1311 server_namec = silc_identifier_check(server_name, strlen(server_name),
1312 SILC_STRING_UTF8, 256, NULL);
1316 silc_idcache_add(conn->internal->server_cache, server_namec,
1317 server_entry->server_id, server_entry);
1322 (!server_entry->server_info ||
1323 !silc_utf8_strcasecmp(server_entry->server_info, server_info))) {
1324 silc_free(server_entry->server_info);
1325 server_entry->server_info = strdup(server_info);
1329 /* Formats the nickname of the client specified by the `client_entry'.
1330 If the format is specified by the application this will format the
1331 nickname and replace the old nickname in the client entry. If the
1332 format string is not specified then this function has no effect. */
1334 void silc_client_nickname_format(SilcClient client,
1335 SilcClientConnection conn,
1336 SilcClientEntry client_entry)
1339 char newnick[128 + 1];
1340 int i, off = 0, len;
1343 SilcClientEntry entry, unformatted = NULL;
1345 SILC_LOG_DEBUG(("Start"));
1347 if (!client->internal->params->nickname_format[0])
1350 if (!client_entry->nickname)
1353 /* Get all clients with same nickname. Do not perform the formatting
1354 if there aren't any clients with same nickname unless the application
1355 is forcing us to do so. */
1356 clients = silc_client_get_clients_local(client, conn,
1357 client_entry->nickname, NULL);
1358 if (!clients && !client->internal->params->nickname_force_format)
1363 while ((entry = silc_dlist_get(clients))) {
1364 if (entry->internal.valid && entry != client_entry)
1366 if (entry->internal.valid && entry != client_entry &&
1367 silc_utf8_strcasecmp(entry->nickname, client_entry->nickname)) {
1369 unformatted = entry;
1372 if (!len || freebase)
1375 /* If we are changing nickname of our local entry we'll enforce
1376 that we will always get the unformatted nickname. Give our
1377 format number to the one that is not formatted now. */
1378 if (unformatted && client_entry == conn->local_entry)
1379 client_entry = unformatted;
1381 memset(newnick, 0, sizeof(newnick));
1382 cp = client->internal->params->nickname_format;
1392 if (!client_entry->nickname[0])
1394 len = strlen(client_entry->nickname);
1395 memcpy(&newnick[off], client_entry->nickname, len);
1399 /* Stripped hostname */
1400 if (!client_entry->hostname[0])
1402 len = strcspn(client_entry->hostname, ".");
1403 i = strcspn(client_entry->hostname, "-");
1406 memcpy(&newnick[off], client_entry->hostname, len);
1411 if (!client_entry->hostname[0])
1413 len = strlen(client_entry->hostname);
1414 memcpy(&newnick[off], client_entry->hostname, len);
1418 /* Stripped server name */
1419 if (!client_entry->server)
1421 len = strcspn(client_entry->server, ".");
1422 memcpy(&newnick[off], client_entry->server, len);
1426 /* Full server name */
1427 if (!client_entry->server)
1429 len = strlen(client_entry->server);
1430 memcpy(&newnick[off], client_entry->server, len);
1434 /* Ascending number */
1439 if (silc_dlist_count(clients) == 1)
1442 silc_dlist_start(clients);
1443 while ((entry = silc_dlist_get(clients))) {
1444 if (!silc_utf8_strncasecmp(entry->nickname, newnick, off))
1446 if (strlen(entry->nickname) <= off)
1448 num = atoi(&entry->nickname[off]);
1453 memset(tmp, 0, sizeof(tmp));
1454 snprintf(tmp, sizeof(tmp) - 1, "%d", ++max);
1456 memcpy(&newnick[off], tmp, len);
1461 /* Some other character in the string */
1462 memcpy(&newnick[off], cp, 1);
1471 memcpy(client_entry->nickname, newnick, strlen(newnick));
1472 silc_dlist_uninit(clients);