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,
50 silc_mutex_unlock(conn->internal->lock);
54 client_entry = id_cache->context;
57 silc_client_ref_client(client, conn, client_entry);
58 silc_mutex_unlock(conn->internal->lock);
60 SILC_LOG_DEBUG(("Found"));
65 /* Finds clients by nickname from local cache. */
67 SilcDList silc_client_get_clients_local(SilcClient client,
68 SilcClientConnection conn,
72 SilcIDCacheEntry id_cache;
75 SilcClientEntry entry;
78 if (!client || !conn || !nickname)
81 SILC_LOG_DEBUG(("Find clients by nickname %s", nickname));
83 /* Normalize nickname for search */
84 nicknamec = silc_identifier_check(nickname, strlen(nickname),
85 SILC_STRING_UTF8, 128, NULL);
89 clients = silc_dlist_init();
95 silc_mutex_lock(conn->internal->lock);
98 silc_list_init(list, struct SilcIDCacheEntryStruct, next);
99 if (!silc_idcache_find_by_name(conn->internal->client_cache, nicknamec,
101 silc_mutex_unlock(conn->internal->lock);
102 silc_free(nicknamec);
103 silc_dlist_uninit(clients);
108 /* Take all without any further checking */
109 silc_list_start(list);
110 while ((id_cache = silc_list_get(list))) {
111 silc_client_ref_client(client, conn, entry);
112 silc_dlist_add(clients, id_cache->context);
115 /* Check multiple cache entries for exact match */
116 silc_list_start(list);
117 while ((id_cache = silc_list_get(list))) {
118 entry = id_cache->context;
119 if (silc_utf8_strcasecmp(entry->nickname, format)) {
120 silc_client_ref_client(client, conn, entry);
121 silc_dlist_add(clients, entry);
126 silc_mutex_unlock(conn->internal->lock);
128 silc_dlist_start(clients);
130 silc_free(nicknamec);
134 /********************** Client Resolving from Server ************************/
136 /* Resolving context */
139 SilcGetClientCallback completion;
141 } *SilcClientGetClientInternal;
143 /* Resolving command callback */
145 static SilcBool silc_client_get_clients_cb(SilcClient client,
146 SilcClientConnection conn,
153 SilcClientGetClientInternal i = context;
154 SilcClientEntry client_entry;
156 if (error != SILC_STATUS_OK) {
157 SILC_LOG_DEBUG(("Resolving failed: %s", silc_get_status_message(error)));
159 i->completion(client, conn, error, NULL, i->context);
163 /* Add the returned client to list */
165 client_entry = va_arg(ap, SilcClientEntry);
166 silc_client_ref_client(client, conn, client_entry);
167 silc_dlist_add(i->clients, client_entry);
168 client_entry->internal.resolve_cmd_ident = 0;
171 if (status == SILC_STATUS_OK || status == SILC_STATUS_LIST_END) {
172 /* Deliver the clients to the caller */
174 SILC_LOG_DEBUG(("Resolved %d clients", silc_dlist_count(i->clients)));
175 silc_dlist_start(i->clients);
176 i->completion(client, conn, SILC_STATUS_OK, i->clients, i->context);
184 silc_client_list_free(client, conn, i->clients);
189 /* Resolves client information from server by the client ID. */
192 silc_client_get_client_by_id_resolve(SilcClient client,
193 SilcClientConnection conn,
194 SilcClientID *client_id,
195 SilcBuffer attributes,
196 SilcGetClientCallback completion,
199 SilcClientGetClientInternal i;
200 SilcClientEntry client_entry;
202 SilcUInt16 cmd_ident;
204 if (!client || !conn | !client_id)
207 SILC_LOG_DEBUG(("Resolve client by ID (%s)",
208 silc_id_render(client_id, SILC_ID_CLIENT)));
210 i = silc_calloc(1, sizeof(*i));
213 i->completion = completion;
214 i->context = context;
215 i->clients = silc_dlist_init();
221 /* Attach to resolving, if on going */
222 client_entry = silc_client_get_client_by_id(client, conn, client_id);
223 if (client_entry && client_entry->internal.resolve_cmd_ident) {
224 SILC_LOG_DEBUG(("Attach to existing resolving"));
225 silc_client_unref_client(client, conn, client_entry);
226 silc_client_command_pending(conn, SILC_COMMAND_NONE,
227 client_entry->internal.resolve_cmd_ident,
228 silc_client_get_clients_cb, i);
229 return client_entry->internal.resolve_cmd_ident;
232 /* Send the command */
233 idp = silc_id_payload_encode(client_id, SILC_ID_CLIENT);
234 cmd_ident = silc_client_command_send(client, conn, SILC_COMMAND_WHOIS,
235 silc_client_get_clients_cb, i,
236 2, 3, silc_buffer_datalen(attributes),
237 4, silc_buffer_datalen(idp));
238 if (!cmd_ident && completion)
239 completion(client, conn, SILC_STATUS_ERR_RESOURCE_LIMIT, NULL, context);
241 if (client_entry && cmd_ident)
242 client_entry->internal.resolve_cmd_ident = cmd_ident;
244 silc_client_unref_client(client, conn, client_entry);
245 silc_buffer_free(idp);
250 /* Finds client entry or entries by the `nickname' and `server'. The
251 completion callback will be called when the client entries has been
252 found. Used internally by the library. */
254 static SilcUInt16 silc_client_get_clients_i(SilcClient client,
255 SilcClientConnection conn,
257 const char *nickname,
259 SilcBuffer attributes,
260 SilcGetClientCallback completion,
263 SilcClientGetClientInternal i;
264 char userhost[768 + 1];
267 SILC_LOG_DEBUG(("Resolve client by %s command",
268 silc_get_command_name(command)));
270 if (!client || !conn)
272 if (!nickname && !attributes)
275 i = silc_calloc(1, sizeof(*i));
278 i->clients = silc_dlist_init();
283 i->completion = completion;
284 i->context = context;
286 memset(userhost, 0, sizeof(userhost));
287 if (nickname && server) {
288 len = strlen(nickname) + strlen(server) + 3;
289 silc_strncat(userhost, len, nickname, strlen(nickname));
290 silc_strncat(userhost, len, "@", 1);
291 silc_strncat(userhost, len, server, strlen(server));
292 } else if (nickname) {
293 silc_strncat(userhost, sizeof(userhost) - 1, nickname, strlen(nickname));
296 /* Send the command */
297 if (command == SILC_COMMAND_IDENTIFY)
298 return silc_client_command_send(client, conn, SILC_COMMAND_IDENTIFY,
299 silc_client_get_clients_cb, i,
300 1, 1, userhost, strlen(userhost));
301 return silc_client_command_send(client, conn, SILC_COMMAND_WHOIS,
302 silc_client_get_clients_cb, i,
303 2, 1, userhost, strlen(userhost),
304 3, silc_buffer_datalen(attributes));
307 /* Get clients from server with IDENTIFY command */
309 SilcUInt16 silc_client_get_clients(SilcClient client,
310 SilcClientConnection conn,
311 const char *nickname,
313 SilcGetClientCallback completion,
316 return silc_client_get_clients_i(client, conn, SILC_COMMAND_IDENTIFY,
317 nickname, server, NULL,
318 completion, context);
321 /* Get clients from server with WHOIS command */
323 SilcUInt16 silc_client_get_clients_whois(SilcClient client,
324 SilcClientConnection conn,
325 const char *nickname,
327 SilcBuffer attributes,
328 SilcGetClientCallback completion,
331 return silc_client_get_clients_i(client, conn, SILC_COMMAND_WHOIS,
332 nickname, server, attributes,
333 completion, context);
336 /* ID list resolving context */
338 SilcGetClientCallback completion;
340 SilcBuffer client_id_list;
341 SilcUInt32 list_count;
342 } *GetClientsByListInternal;
344 static SilcBool silc_client_get_clients_list_cb(SilcClient client,
345 SilcClientConnection conn,
352 GetClientsByListInternal i = context;
353 SilcClientEntry client_entry;
359 /* Process the list after all replies have been received */
360 if (status != SILC_STATUS_OK && !SILC_STATUS_IS_ERROR(status) &&
361 status != SILC_STATUS_LIST_END)
364 SILC_LOG_DEBUG(("Resolved all clients"));
366 clients = silc_dlist_init();
368 status = SILC_STATUS_ERR_RESOURCE_LIMIT;
372 for (c = 0; c < i->list_count; c++) {
374 SILC_GET16_MSB(idp_len, i->client_id_list->data + 2);
376 if (!silc_id_payload_parse_id(i->client_id_list->data, idp_len, &id)) {
377 status = SILC_STATUS_ERR_BAD_CLIENT_ID;
381 /* Get client entry */
382 client_entry = silc_client_get_client_by_id(client, conn, &id.u.client_id);
384 silc_dlist_add(clients, client_entry);
386 if (!silc_buffer_pull(i->client_id_list, idp_len)) {
387 status = SILC_STATUS_ERR_BAD_CLIENT_ID;
392 silc_dlist_start(clients);
393 status = SILC_STATUS_OK;
395 i->completion(client, conn, status, clients, i->context);
398 if (status != SILC_STATUS_OK && i->completion)
399 i->completion(client, conn, status, NULL, i->context);
400 silc_client_list_free(client, conn, clients);
405 /* Gets client entries by the list of client ID's `client_id_list'. This
406 always resolves those client ID's it does not know yet from the server
407 so this function might take a while. The `client_id_list' is a list
408 of ID Payloads added one after other. JOIN command reply and USERS
409 command reply for example returns this sort of list. The `completion'
410 will be called after the entries are available. */
412 SilcUInt16 silc_client_get_clients_by_list(SilcClient client,
413 SilcClientConnection conn,
414 SilcUInt32 list_count,
415 SilcBuffer client_id_list,
416 SilcGetClientCallback completion,
419 GetClientsByListInternal in;
420 SilcClientEntry entry;
421 unsigned char **res_argv = NULL;
422 SilcUInt32 *res_argv_lens = NULL, *res_argv_types = NULL, res_argc = 0;
423 SilcUInt16 idp_len, cmd_ident;
427 SILC_LOG_DEBUG(("Resolve clients from Client ID list"));
429 if (!client || !conn || !client_id_list)
432 in = silc_calloc(1, sizeof(*in));
435 in->completion = completion;
436 in->context = context;
437 in->list_count = list_count;
438 in->client_id_list = silc_buffer_copy(client_id_list);
439 if (!in->client_id_list)
442 for (i = 0; i < list_count; i++) {
444 SILC_GET16_MSB(idp_len, client_id_list->data + 2);
446 if (!silc_id_payload_parse_id(client_id_list->data, idp_len, &id))
449 /* Check if we have this client cached already. If we don't have the
450 entry or it has incomplete info, then resolve it from the server. */
451 entry = silc_client_get_client_by_id(client, conn, &id.u.client_id);
452 if (!entry || !entry->nickname[0] || !entry->username[0] ||
455 res_argv = silc_calloc(list_count, sizeof(*res_argv));
456 res_argv_lens = silc_calloc(list_count, sizeof(*res_argv_lens));
457 res_argv_types = silc_calloc(list_count, sizeof(*res_argv_types));
458 if (!res_argv || !res_argv_lens || !res_argv_types) {
459 silc_client_unref_client(client, conn, entry);
464 res_argv[res_argc] = client_id_list->data;
465 res_argv_lens[res_argc] = idp_len;
466 res_argv_types[res_argc] = res_argc + 4;
469 silc_client_unref_client(client, conn, entry);
471 if (!silc_buffer_pull(client_id_list, idp_len))
474 silc_buffer_start(client_id_list);
476 /* Query the unknown client information from server */
478 cmd_ident = silc_client_command_send_argv(client,
479 conn, SILC_COMMAND_WHOIS,
480 silc_client_get_clients_list_cb,
481 in, res_argc, res_argv,
485 silc_free(res_argv_lens);
486 silc_free(res_argv_types);
490 /* We have the clients in cache, get them and call the completion */
491 silc_client_get_clients_list_cb(client, conn, SILC_COMMAND_WHOIS,
492 SILC_STATUS_OK, SILC_STATUS_OK, in, NULL);
496 silc_buffer_free(in->client_id_list);
499 silc_free(res_argv_lens);
500 silc_free(res_argv_types);
507 SilcClientConnection conn;
508 SilcChannelID channel_id;
509 SilcGetClientCallback completion;
512 } *GetClientsByChannelInternal;
514 SILC_CLIENT_CMD_FUNC(get_clients_by_channel_cb)
516 GetClientsByChannelInternal i = context;
517 SilcClientEntry *clients = NULL;
518 SilcUInt32 clients_count = 0;
519 SilcBool found = FALSE;
520 SilcChannelEntry channel;
521 SilcHashTableList htl;
530 channel = silc_client_get_channel_by_id(i->client, i->conn, &i->channel_id);
531 if (channel && !silc_hash_table_count(channel->user_list)) {
532 clients = silc_calloc(silc_hash_table_count(channel->user_list),
534 silc_hash_table_list(channel->user_list, &htl);
535 while (silc_hash_table_get(&htl, NULL, (void *)&chu))
536 clients[clients_count++] = chu->client;
537 silc_hash_table_list_reset(&htl);
542 i->completion(i->client, i->conn, clients, clients_count, i->context);
545 i->completion(i->client, i->conn, NULL, 0, i->context);
551 /* Gets client entries by the channel entry indicated by `channel'. Thus,
552 it resolves the clients currently on that channel. */
554 void silc_client_get_clients_by_channel(SilcClient client,
555 SilcClientConnection conn,
556 SilcChannelEntry channel,
557 SilcGetClientCallback completion,
560 GetClientsByChannelInternal in;
561 SilcHashTableList htl;
563 SilcClientEntry entry;
564 unsigned char **res_argv = NULL;
565 SilcUInt32 *res_argv_lens = NULL, *res_argv_types = NULL, res_argc = 0;
567 SilcBool wait_res = FALSE;
569 assert(client && conn && channel);
571 SILC_LOG_DEBUG(("Start"));
573 in = silc_calloc(1, sizeof(*in));
576 in->channel_id = *channel->id;
577 in->completion = completion;
578 in->context = context;
580 /* If user list does not exist, send USERS command. */
581 if (!channel->user_list || !silc_hash_table_count(channel->user_list)) {
582 SILC_LOG_DEBUG(("Sending USERS"));
583 silc_client_command_register(client, SILC_COMMAND_USERS, NULL, NULL,
584 silc_client_command_reply_users_i, 0,
586 silc_client_command_send(client, conn, SILC_COMMAND_USERS,
587 conn->cmd_ident, 1, 2, channel->channel_name,
588 strlen(channel->channel_name));
589 silc_client_command_pending(conn, SILC_COMMAND_USERS, conn->cmd_ident,
590 silc_client_command_get_clients_by_channel_cb,
595 silc_hash_table_list(channel->user_list, &htl);
596 while (silc_hash_table_get(&htl, NULL, (void *)&chu)) {
599 /* If the entry has incomplete info, then resolve it from the server. */
600 if (!entry->nickname[0] || !entry->realname) {
601 if (entry->status & SILC_CLIENT_STATUS_RESOLVING) {
602 /* Attach to this resolving and wait until it finishes */
603 silc_client_command_pending(
604 conn, SILC_COMMAND_NONE,
605 entry->resolve_cmd_ident,
606 silc_client_command_get_clients_by_channel_cb,
612 entry->status |= SILC_CLIENT_STATUS_RESOLVING;
613 entry->resolve_cmd_ident = conn->cmd_ident + 1;
615 idp = silc_id_payload_encode(entry->id, SILC_ID_CLIENT);
617 /* No we don't have it, query it from the server. Assemble argument
618 table that will be sent for the WHOIS command later. */
619 res_argv = silc_realloc(res_argv, sizeof(*res_argv) *
621 res_argv_lens = silc_realloc(res_argv_lens, sizeof(*res_argv_lens) *
623 res_argv_types = silc_realloc(res_argv_types, sizeof(*res_argv_types) *
625 res_argv[res_argc] = silc_memdup(idp->data, idp->len);
626 res_argv_lens[res_argc] = idp->len;
627 res_argv_types[res_argc] = res_argc + 4;
630 silc_buffer_free(idp);
633 silc_hash_table_list_reset(&htl);
635 /* Query the client information from server if the list included clients
636 that we don't know about. */
640 /* Send the WHOIS command to server */
641 res_cmd = silc_command_payload_encode(SILC_COMMAND_WHOIS,
642 res_argc, res_argv, res_argv_lens,
643 res_argv_types, ++conn->cmd_ident);
644 silc_client_packet_send(client, conn->sock, SILC_PACKET_COMMAND,
645 NULL, 0, NULL, NULL, res_cmd->data, res_cmd->len,
648 /* Register our own command reply for this command */
649 silc_client_command_register(client, SILC_COMMAND_WHOIS, NULL, NULL,
650 silc_client_command_reply_whois_i, 0,
653 /* Process the applications request after reply has been received */
654 silc_client_command_pending(
655 conn, SILC_COMMAND_WHOIS, conn->cmd_ident,
656 silc_client_command_get_clients_by_channel_cb,
660 silc_buffer_free(res_cmd);
662 silc_free(res_argv_lens);
663 silc_free(res_argv_types);
670 /* We have the clients in cache, get them and call the completion */
671 silc_client_command_get_clients_by_channel_cb((void *)in, NULL);
676 /************************** Client Entry Routines ***************************/
678 /* Creates new client entry and adds it to the ID cache. Returns pointer
681 SilcClientEntry silc_client_add_client(SilcClient client,
682 SilcClientConnection conn,
683 char *nickname, char *username,
684 char *userinfo, SilcClientID *id,
687 SilcClientEntry client_entry;
690 SILC_LOG_DEBUG(("Adding new client entry"));
692 /* Save the client infos */
693 client_entry = silc_calloc(1, sizeof(*client_entry));
697 silc_atomic_init8(&client_entry->internal.refcnt, 0);
698 client_entry->id = *id;
699 client_entry->internal.valid = TRUE;
700 client_entry->mode = mode;
701 client_entry->realname = userinfo ? strdup(userinfo) : NULL;
702 silc_parse_userfqdn(nickname, client_entry->nickname,
703 sizeof(client_entry->nickname),
704 client_entry->server,
705 sizeof(client_entry->server));
706 silc_parse_userfqdn(username, client_entry->username,
707 sizeof(client_entry->username),
708 client_entry->hostname,
709 sizeof(client_entry->hostname));
710 client_entry->channels = silc_hash_table_alloc(1, silc_hash_ptr, NULL, NULL,
711 NULL, NULL, NULL, TRUE);
712 if (!client_entry->channels) {
713 silc_free(client_entry->realname);
714 silc_free(client_entry);
718 /* Normalize nickname */
719 if (client_entry->nickname[0]) {
720 nick = silc_identifier_check(client_entry->nickname,
721 strlen(client_entry->nickname),
722 SILC_STRING_UTF8, 128, NULL);
724 silc_free(client_entry->realname);
725 silc_hash_table_free(client_entry->channels);
726 silc_free(client_entry);
731 /* Format the nickname */
732 silc_client_nickname_format(client, conn, client_entry);
734 silc_mutex_lock(conn->internal->lock);
736 /* Add client to cache, the normalized nickname is saved to cache */
737 if (!silc_idcache_add(conn->internal->client_cache, nick,
738 &client_entry->id, client_entry)) {
740 silc_free(client_entry->realname);
741 silc_hash_table_free(client_entry->channels);
742 silc_free(client_entry);
743 silc_mutex_unlock(conn->internal->lock);
747 client_entry->nickname_normalized = nick;
749 silc_mutex_unlock(conn->internal->lock);
750 silc_client_ref_client(client, conn, client_entry);
755 /* Updates the `client_entry' with the new information sent as argument. */
757 void silc_client_update_client(SilcClient client,
758 SilcClientConnection conn,
759 SilcClientEntry client_entry,
760 const char *nickname,
761 const char *username,
762 const char *userinfo,
767 SILC_LOG_DEBUG(("Update client entry"));
769 if (!client_entry->realname && userinfo)
770 client_entry->realname = strdup(userinfo);
771 if ((!client_entry->username[0] || !client_entry->hostname[0]) && username)
772 silc_parse_userfqdn(username, client_entry->username,
773 sizeof(client_entry->username),
774 client_entry->hostname,
775 sizeof(client_entry->username));
776 if (!client_entry->nickname[0] && nickname) {
777 silc_parse_userfqdn(nickname, client_entry->nickname,
778 sizeof(client_entry->nickname),
779 client_entry->server,
780 sizeof(client_entry->server));
782 /* Normalize nickname */
783 nick = silc_identifier_check(client_entry->nickname,
784 strlen(client_entry->nickname),
785 SILC_STRING_UTF8, 128, NULL);
789 /* Format nickname */
790 silc_client_nickname_format(client, conn, client_entry);
792 /* Remove the old cache entry and create a new one */
793 silc_idcache_del_by_context(conn->internal->client_cache, client_entry,
795 silc_idcache_add(conn->internal->client_cache, nick, &client_entry->id,
798 client_entry->mode = mode;
801 /* Deletes the client entry and frees all memory. */
803 void silc_client_del_client_entry(SilcClient client,
804 SilcClientConnection conn,
805 SilcClientEntry client_entry)
807 SILC_LOG_DEBUG(("Start"));
809 silc_free(client_entry->realname);
810 if (client_entry->public_key)
811 silc_pkcs_public_key_free(client_entry->public_key);
812 silc_hash_table_free(client_entry->channels);
813 if (client_entry->internal.send_key)
814 silc_cipher_free(client_entry->internal.send_key);
815 if (client_entry->internal.receive_key)
816 silc_cipher_free(client_entry->internal.receive_key);
817 silc_free(client_entry->internal.key);
819 silc_client_ftp_session_free_client(conn, client_entry);
820 if (client_entry->internal->ke)
821 silc_client_abort_key_agreement(client, conn, client_entry);
823 silc_free(client_entry);
826 /* Removes client from the cache by the client entry. */
828 SilcBool silc_client_del_client(SilcClient client, SilcClientConnection conn,
829 SilcClientEntry client_entry)
831 SilcBool ret = silc_idcache_del_by_context(conn->internal->client_cache,
835 /* Remove from channels */
836 silc_client_remove_from_channels(client, conn, client_entry);
838 /* Free the client entry data */
839 silc_client_del_client_entry(client, conn, client_entry);
846 /* Take reference of client entry */
848 void silc_client_ref_client(SilcClient client, SilcClientConnection conn,
849 SilcClientEntry client_entry)
851 silc_atomic_add_int8(&client_entry->internal.refcnt, 1);
852 SILC_LOG_DEBUG(("Client %p refcnt %d->%d", client_entry,
853 silc_atomic_get_int8(&client_entry->internal.refcnt) - 1,
854 silc_atomic_get_int8(&client_entry->internal.refcnt)));
857 /* Release reference of client entry */
859 void silc_client_unref_client(SilcClient client, SilcClientConnection conn,
860 SilcClientEntry client_entry)
863 SILC_LOG_DEBUG(("Client %p refcnt %d->%d", client_entry,
864 silc_atomic_get_int8(&client_entry->internal.refcnt),
865 silc_atomic_get_int8(&client_entry->internal.refcnt) - 1));
867 silc_atomic_sub_int8(&client_entry->internal.refcnt, 1) == 0)
868 silc_client_del_client(client, conn, client_entry);
871 /* Free client entry list */
873 void silc_client_list_free(SilcClient client, SilcClientConnection conn,
874 SilcDList client_list)
876 SilcClientEntry client_entry;
879 silc_dlist_start(client_list);
880 while ((client_entry = silc_dlist_get(client_list)))
881 silc_client_unref_client(client, conn, client_entry);
883 silc_dlist_uninit(client_list);
888 /* Formats the nickname of the client specified by the `client_entry'.
889 If the format is specified by the application this will format the
890 nickname and replace the old nickname in the client entry. If the
891 format string is not specified then this function has no effect. */
893 void silc_client_nickname_format(SilcClient client,
894 SilcClientConnection conn,
895 SilcClientEntry client_entry)
898 char newnick[128 + 1];
902 SilcClientEntry entry, unformatted = NULL;
904 SILC_LOG_DEBUG(("Start"));
906 if (!client->internal->params->nickname_format[0])
909 if (!client_entry->nickname[0])
912 /* Get all clients with same nickname. Do not perform the formatting
913 if there aren't any clients with same nickname unless the application
914 is forcing us to do so. */
915 clients = silc_client_get_clients_local(client, conn,
916 client_entry->nickname, NULL);
917 if (!clients && !client->internal->params->nickname_force_format)
922 while ((entry = silc_dlist_get(clients))) {
923 if (entry->internal.valid && entry != client_entry)
925 if (entry->internal.valid && entry != client_entry &&
926 silc_utf8_strcasecmp(entry->nickname, client_entry->nickname)) {
931 if (!len || freebase)
934 /* If we are changing nickname of our local entry we'll enforce
935 that we will always get the unformatted nickname. Give our
936 format number to the one that is not formatted now. */
937 if (unformatted && client_entry == conn->local_entry)
938 client_entry = unformatted;
940 memset(newnick, 0, sizeof(newnick));
941 cp = client->internal->params->nickname_format;
951 if (!client_entry->nickname[0])
953 len = strlen(client_entry->nickname);
954 memcpy(&newnick[off], client_entry->nickname, len);
958 /* Stripped hostname */
959 if (!client_entry->hostname[0])
961 len = strcspn(client_entry->hostname, ".");
962 i = strcspn(client_entry->hostname, "-");
965 memcpy(&newnick[off], client_entry->hostname, len);
970 if (!client_entry->hostname[0])
972 len = strlen(client_entry->hostname);
973 memcpy(&newnick[off], client_entry->hostname, len);
977 /* Stripped server name */
978 if (!client_entry->server)
980 len = strcspn(client_entry->server, ".");
981 memcpy(&newnick[off], client_entry->server, len);
985 /* Full server name */
986 if (!client_entry->server)
988 len = strlen(client_entry->server);
989 memcpy(&newnick[off], client_entry->server, len);
993 /* Ascending number */
998 if (silc_dlist_count(clients) == 1)
1001 silc_dlist_start(clients);
1002 while ((entry = silc_dlist_get(clients))) {
1003 if (!silc_utf8_strncasecmp(entry->nickname, newnick, off))
1005 if (strlen(entry->nickname) <= off)
1007 num = atoi(&entry->nickname[off]);
1012 memset(tmp, 0, sizeof(tmp));
1013 snprintf(tmp, sizeof(tmp) - 1, "%d", ++max);
1015 memcpy(&newnick[off], tmp, len);
1020 /* Some other character in the string */
1021 memcpy(&newnick[off], cp, 1);
1030 memcpy(client_entry->nickname, newnick, strlen(newnick));
1031 silc_client_list_free(client, conn, clients);
1034 /************************ Channel Searching Locally *************************/
1036 /* Finds entry for channel by the channel name. Returns the entry or NULL
1037 if the entry was not found. It is found only if the client is joined
1040 SilcChannelEntry silc_client_get_channel(SilcClient client,
1041 SilcClientConnection conn,
1044 SilcIDCacheEntry id_cache;
1045 SilcChannelEntry entry;
1047 if (!client || !conn || !channel)
1050 SILC_LOG_DEBUG(("Find channel %s", channel));
1052 /* Normalize name for search */
1053 channel = silc_channel_name_check(channel, strlen(channel), SILC_STRING_UTF8,
1058 silc_mutex_lock(conn->internal->lock);
1060 if (!silc_idcache_find_by_name_one(conn->internal->channel_cache, channel,
1062 silc_mutex_unlock(conn->internal->lock);
1067 SILC_LOG_DEBUG(("Found"));
1069 entry = id_cache->context;
1072 silc_client_ref_channel(client, conn, entry);
1073 silc_mutex_unlock(conn->internal->lock);
1080 /* Finds entry for channel by the channel ID. Returns the entry or NULL
1081 if the entry was not found. It is found only if the client is joined
1084 SilcChannelEntry silc_client_get_channel_by_id(SilcClient client,
1085 SilcClientConnection conn,
1086 SilcChannelID *channel_id)
1088 SilcIDCacheEntry id_cache;
1089 SilcChannelEntry entry;
1091 if (!client || !conn || !channel_id)
1094 SILC_LOG_DEBUG(("Find channel by id %s",
1095 silc_id_render(channel_id, SILC_ID_CHANNEL)));
1097 silc_mutex_lock(conn->internal->lock);
1099 if (!silc_idcache_find_by_id_one(conn->internal->channel_cache, channel_id,
1101 silc_mutex_unlock(conn->internal->lock);
1105 SILC_LOG_DEBUG(("Found"));
1107 entry = id_cache->context;
1110 silc_client_ref_channel(client, conn, entry);
1111 silc_mutex_unlock(conn->internal->lock);
1116 /********************** Channel Resolving from Server ***********************/
1118 /* Channel resolving context */
1121 SilcGetChannelCallback completion;
1123 } *SilcClientGetChannelInternal;
1125 /* Resolving command callback */
1127 static SilcBool silc_client_get_channel_cb(SilcClient client,
1128 SilcClientConnection conn,
1129 SilcCommand command,
1135 SilcClientGetChannelInternal i = context;
1136 SilcChannelEntry entry;
1138 if (error != SILC_STATUS_OK) {
1139 SILC_LOG_DEBUG(("Resolving failed: %s", silc_get_status_message(error)));
1141 i->completion(client, conn, error, NULL, i->context);
1145 /* Add the returned channel to list */
1146 if (i->completion) {
1147 entry = va_arg(ap, SilcChannelEntry);
1148 silc_client_ref_channel(client, conn, entry);
1149 silc_dlist_add(i->channels, entry);
1152 if (status == SILC_STATUS_OK || status == SILC_STATUS_LIST_END) {
1153 /* Deliver the channels to the caller */
1154 if (i->completion) {
1155 SILC_LOG_DEBUG(("Resolved %d channels", silc_dlist_count(i->channels)));
1156 silc_dlist_start(i->channels);
1157 i->completion(client, conn, SILC_STATUS_OK, i->channels, i->context);
1165 silc_client_list_free_channels(client, conn, i->channels);
1170 /* Resolves channel entry from the server by the channel name. */
1172 void silc_client_get_channel_resolve(SilcClient client,
1173 SilcClientConnection conn,
1175 SilcGetChannelCallback completion,
1178 SilcClientGetChannelInternal i;
1180 if (!client || !conn || !channel_name || !completion)
1183 SILC_LOG_DEBUG(("Resolve channel %s", channel_name));
1185 i = silc_calloc(1, sizeof(*i));
1188 i->completion = completion;
1189 i->context = context;
1190 i->channels = silc_dlist_init();
1196 /* Send the command */
1197 if (!silc_client_command_send(client, conn, SILC_COMMAND_IDENTIFY,
1198 silc_client_get_channel_cb, i, 1,
1199 3, channel_name, strlen(channel_name))) {
1201 completion(client, conn, SILC_STATUS_ERR_RESOURCE_LIMIT, NULL, context);
1205 /* Resolves channel information from the server by the channel ID. */
1208 silc_client_get_channel_by_id_resolve(SilcClient client,
1209 SilcClientConnection conn,
1210 SilcChannelID *channel_id,
1211 SilcGetChannelCallback completion,
1214 SilcClientGetChannelInternal i;
1216 SilcUInt16 cmd_ident;
1218 if (!client || !conn || !channel_id || !completion)
1221 SILC_LOG_DEBUG(("Resolve channel by id %s",
1222 silc_id_render(channel_id, SILC_ID_CHANNEL)));
1224 i = silc_calloc(1, sizeof(*i));
1227 i->completion = completion;
1228 i->context = context;
1229 i->channels = silc_dlist_init();
1235 /* Send the command */
1236 idp = silc_id_payload_encode(channel_id, SILC_ID_CHANNEL);
1237 cmd_ident = silc_client_command_send(client, conn, SILC_COMMAND_IDENTIFY,
1238 silc_client_get_channel_cb, i, 1,
1239 5, silc_buffer_datalen(idp));
1240 silc_buffer_free(idp);
1241 if (!cmd_ident && completion)
1242 completion(client, conn, SILC_STATUS_ERR_RESOURCE_LIMIT, NULL, context);
1247 /************************* Channel Entry Routines ***************************/
1249 /* Add new channel entry to the ID Cache */
1251 SilcChannelEntry silc_client_add_channel(SilcClient client,
1252 SilcClientConnection conn,
1253 const char *channel_name,
1255 SilcChannelID *channel_id)
1257 SilcChannelEntry channel;
1258 char *channel_namec;
1260 SILC_LOG_DEBUG(("Start"));
1262 channel = silc_calloc(1, sizeof(*channel));
1266 silc_atomic_init8(&channel->internal.refcnt, 0);
1267 channel->id = *channel_id;
1268 channel->mode = mode;
1270 channel->channel_name = strdup(channel_name);
1271 if (!channel->channel_name) {
1276 channel->user_list = silc_hash_table_alloc(1, silc_hash_ptr, NULL, NULL,
1277 NULL, NULL, NULL, TRUE);
1278 if (!channel->user_list) {
1279 silc_free(channel->channel_name);
1284 /* Normalize channel name */
1285 channel_namec = silc_channel_name_check(channel_name, strlen(channel_name),
1286 SILC_STRING_UTF8, 256, NULL);
1287 if (!channel_namec) {
1288 silc_free(channel->channel_name);
1289 silc_hash_table_free(channel->user_list);
1294 silc_mutex_lock(conn->internal->lock);
1296 /* Add channel to cache, the normalized channel name is saved to cache */
1297 if (!silc_idcache_add(conn->internal->channel_cache, channel_namec,
1298 &channel->id, channel)) {
1299 silc_free(channel_namec);
1300 silc_free(channel->channel_name);
1301 silc_hash_table_free(channel->user_list);
1303 silc_mutex_unlock(conn->internal->lock);
1307 silc_mutex_unlock(conn->internal->lock);
1308 silc_client_ref_channel(client, conn, channel);
1313 /* Foreach callbcak to free all users from the channel when deleting a
1316 static void silc_client_del_channel_foreach(void *key, void *context,
1319 SilcChannelUser chu = (SilcChannelUser)context;
1321 SILC_LOG_DEBUG(("Start"));
1323 /* Remove the context from the client's channel hash table as that
1324 table and channel's user_list hash table share this same context. */
1325 silc_hash_table_del(chu->client->channels, chu->channel);
1329 /* Removes channel from the cache by the channel entry. */
1331 SilcBool silc_client_del_channel(SilcClient client, SilcClientConnection conn,
1332 SilcChannelEntry channel)
1334 SilcBool ret = silc_idcache_del_by_context(conn->internal->channel_cache,
1338 SILC_LOG_DEBUG(("Start"));
1340 /* Free all client entrys from the users list. The silc_hash_table_free
1341 will free all the entries so they are not freed at the foreach
1343 silc_hash_table_foreach(channel->user_list, silc_client_del_channel_foreach,
1345 silc_hash_table_free(channel->user_list);
1347 silc_free(channel->channel_name);
1348 silc_free(channel->topic);
1349 if (channel->founder_key)
1350 silc_pkcs_public_key_free(channel->founder_key);
1351 silc_free(channel->key);
1352 if (channel->channel_key)
1353 silc_cipher_free(channel->channel_key);
1355 silc_hmac_free(channel->hmac);
1356 if (channel->old_channel_keys) {
1358 silc_dlist_start(channel->old_channel_keys);
1359 while ((key = silc_dlist_get(channel->old_channel_keys)) != SILC_LIST_END)
1360 silc_cipher_free(key);
1361 silc_dlist_uninit(channel->old_channel_keys);
1363 if (channel->old_hmacs) {
1365 silc_dlist_start(channel->old_hmacs);
1366 while ((hmac = silc_dlist_get(channel->old_hmacs)) != SILC_LIST_END)
1367 silc_hmac_free(hmac);
1368 silc_dlist_uninit(channel->old_hmacs);
1370 silc_schedule_task_del_by_context(conn->client->schedule, channel);
1371 silc_client_del_channel_private_keys(client, conn, channel);
1377 /* Replaces the channel ID of the `channel' to `new_id'. Returns FALSE
1378 if the ID could not be changed. */
1380 SilcBool silc_client_replace_channel_id(SilcClient client,
1381 SilcClientConnection conn,
1382 SilcChannelEntry channel,
1383 SilcChannelID *new_id)
1385 SilcIDCacheEntry id_cache;
1386 SilcBool ret = FALSE;
1391 SILC_LOG_DEBUG(("Old Channel ID id(%s)",
1392 silc_id_render(&channel->id, SILC_ID_CHANNEL)));
1393 SILC_LOG_DEBUG(("New Channel ID id(%s)",
1394 silc_id_render(new_id, SILC_ID_CHANNEL)));
1396 silc_mutex_lock(conn->internal->lock);
1399 if (silc_idcache_find_by_id_one(conn->internal->channel_cache,
1400 &channel->id, &id_cache))
1401 ret = silc_idcache_update(conn->internal->channel_cache, id_cache,
1402 &channel->id, new_id, NULL, NULL, FALSE);
1404 silc_mutex_unlock(conn->internal->lock);
1409 /* Take reference of channel entry */
1411 void silc_client_ref_channel(SilcClient client, SilcClientConnection conn,
1412 SilcChannelEntry channel_entry)
1414 silc_atomic_add_int8(&channel_entry->internal.refcnt, 1);
1417 /* Release reference of channel entry */
1419 void silc_client_unref_channel(SilcClient client, SilcClientConnection conn,
1420 SilcChannelEntry channel_entry)
1422 if (channel_entry &&
1423 silc_atomic_sub_int8(&channel_entry->internal.refcnt, 1) == 0)
1424 silc_client_del_channel(client, conn, channel_entry);
1427 /* Free channel entry list */
1429 void silc_client_list_free_channels(SilcClient client,
1430 SilcClientConnection conn,
1431 SilcDList channel_list)
1433 SilcChannelEntry channel_entry;
1436 silc_dlist_start(channel_list);
1437 while ((channel_entry = silc_dlist_get(channel_list)))
1438 silc_client_unref_channel(client, conn, channel_entry);
1440 silc_dlist_uninit(channel_list);
1444 /************************* Server Searching Locally *************************/
1446 /* Finds entry for server by the server name. */
1448 SilcServerEntry silc_client_get_server(SilcClient client,
1449 SilcClientConnection conn,
1452 SilcIDCacheEntry id_cache;
1453 SilcServerEntry entry;
1455 if (!client || !conn || !server_name)
1458 SILC_LOG_DEBUG(("Find server by name %s", server_name));
1460 /* Normalize server name for search */
1461 server_name = silc_identifier_check(server_name, strlen(server_name),
1462 SILC_STRING_UTF8, 256, NULL);
1466 silc_mutex_lock(conn->internal->lock);
1468 if (!silc_idcache_find_by_name_one(conn->internal->server_cache,
1469 server_name, &id_cache)) {
1470 silc_free(server_name);
1474 SILC_LOG_DEBUG(("Found"));
1477 entry = id_cache->context;
1478 silc_client_ref_server(client, conn, entry);
1480 silc_mutex_unlock(conn->internal->lock);
1482 silc_free(server_name);
1487 /* Finds entry for server by the server ID. */
1489 SilcServerEntry silc_client_get_server_by_id(SilcClient client,
1490 SilcClientConnection conn,
1491 SilcServerID *server_id)
1493 SilcIDCacheEntry id_cache;
1494 SilcServerEntry entry;
1496 if (!client || !conn || !server_id)
1499 SILC_LOG_DEBUG(("Find server by id %s",
1500 silc_id_render(server_id, SILC_ID_SERVER)));
1502 silc_mutex_lock(conn->internal->lock);
1504 if (!silc_idcache_find_by_id_one(conn->internal->server_cache,
1505 server_id, &id_cache))
1508 SILC_LOG_DEBUG(("Found"));
1511 entry = id_cache->context;
1512 silc_client_ref_server(client, conn, entry);
1514 silc_mutex_unlock(conn->internal->lock);
1519 /*********************** Server Resolving from Server ***********************/
1521 /* Resolving context */
1524 SilcGetServerCallback completion;
1526 } *SilcClientGetServerInternal;
1528 /* Resolving command callback */
1530 static SilcBool silc_client_get_server_cb(SilcClient client,
1531 SilcClientConnection conn,
1532 SilcCommand command,
1538 SilcClientGetServerInternal i = context;
1539 SilcServerEntry server;
1541 if (error != SILC_STATUS_OK) {
1542 SILC_LOG_DEBUG(("Resolving failed: %s", silc_get_status_message(error)));
1544 i->completion(client, conn, error, NULL, i->context);
1548 /* Add the returned servers to list */
1549 if (i->completion) {
1550 server = va_arg(ap, SilcServerEntry);
1551 silc_client_ref_server(client, conn, server);
1552 silc_dlist_add(i->servers, server);
1553 server->internal.resolve_cmd_ident = 0;
1556 if (status == SILC_STATUS_OK || status == SILC_STATUS_LIST_END) {
1557 /* Deliver the servers to the caller */
1558 if (i->completion) {
1559 SILC_LOG_DEBUG(("Resolved %d servers", silc_dlist_count(i->servers)));
1560 silc_dlist_start(i->servers);
1561 i->completion(client, conn, SILC_STATUS_OK, i->servers, i->context);
1569 silc_client_list_free_servers(client, conn, i->servers);
1574 /* Resolve server by server ID */
1577 silc_client_get_server_by_id_resolve(SilcClient client,
1578 SilcClientConnection conn,
1579 SilcServerID *server_id,
1580 SilcGetServerCallback completion,
1583 SilcClientGetServerInternal i;
1584 SilcServerEntry server;
1586 SilcUInt16 cmd_ident;
1588 if (!client || !conn || !server_id || !completion)
1591 SILC_LOG_DEBUG(("Resolve server by id %s",
1592 silc_id_render(server_id, SILC_ID_SERVER)));
1594 i = silc_calloc(1, sizeof(*i));
1597 i->completion = completion;
1598 i->context = context;
1599 i->servers = silc_dlist_init();
1605 /* Attach to resolving, if on going */
1606 server = silc_client_get_server_by_id(client, conn, server_id);
1607 if (server && server->internal.resolve_cmd_ident) {
1608 SILC_LOG_DEBUG(("Attach to existing resolving"));
1609 silc_client_unref_server(client, conn, server);
1610 silc_client_command_pending(conn, SILC_COMMAND_NONE,
1611 server->internal.resolve_cmd_ident,
1612 silc_client_get_server_cb, i);
1613 return server->internal.resolve_cmd_ident;
1616 /* Send the command */
1617 idp = silc_id_payload_encode(server_id, SILC_ID_SERVER);
1618 cmd_ident = silc_client_command_send(client, conn, SILC_COMMAND_IDENTIFY,
1619 silc_client_get_server_cb, i, 1,
1620 5, silc_buffer_datalen(idp));
1621 silc_buffer_free(idp);
1622 if (!cmd_ident && completion)
1623 completion(client, conn, SILC_STATUS_ERR_RESOURCE_LIMIT, NULL, context);
1625 if (server && cmd_ident)
1626 server->internal.resolve_cmd_ident = cmd_ident;
1628 silc_client_unref_server(client, conn, server);
1633 /************************** Server Entry Routines ***************************/
1635 /* Add new server entry */
1637 SilcServerEntry silc_client_add_server(SilcClient client,
1638 SilcClientConnection conn,
1639 const char *server_name,
1640 const char *server_info,
1641 SilcServerID *server_id)
1643 SilcServerEntry server_entry;
1644 char *server_namec = NULL;
1646 SILC_LOG_DEBUG(("Start"));
1648 server_entry = silc_calloc(1, sizeof(*server_entry));
1649 if (!server_entry || !server_id)
1652 silc_atomic_init8(&server_entry->internal.refcnt, 0);
1653 server_entry->id = *server_id;
1655 server_entry->server_name = strdup(server_name);
1657 server_entry->server_info = strdup(server_info);
1659 /* Normalize server name */
1661 server_namec = silc_identifier_check(server_name, strlen(server_name),
1662 SILC_STRING_UTF8, 256, NULL);
1663 if (!server_namec) {
1664 silc_free(server_entry->server_name);
1665 silc_free(server_entry->server_info);
1666 silc_free(server_entry);
1671 silc_mutex_lock(conn->internal->lock);
1673 /* Add server to cache */
1674 if (!silc_idcache_add(conn->internal->server_cache, server_namec,
1675 &server_entry->id, server_entry)) {
1676 silc_free(server_namec);
1677 silc_free(server_entry->server_name);
1678 silc_free(server_entry->server_info);
1679 silc_free(server_entry);
1680 silc_mutex_unlock(conn->internal->lock);
1684 silc_mutex_unlock(conn->internal->lock);
1685 silc_client_ref_server(client, conn, server_entry);
1687 return server_entry;
1690 /* Removes server from the cache by the server entry. */
1692 SilcBool silc_client_del_server(SilcClient client, SilcClientConnection conn,
1693 SilcServerEntry server)
1695 SilcBool ret = silc_idcache_del_by_context(conn->internal->server_cache,
1697 silc_free(server->server_name);
1698 silc_free(server->server_info);
1703 /* Updates the `server_entry' with the new information sent as argument. */
1705 void silc_client_update_server(SilcClient client,
1706 SilcClientConnection conn,
1707 SilcServerEntry server_entry,
1708 const char *server_name,
1709 const char *server_info)
1711 char *server_namec = NULL;
1713 SILC_LOG_DEBUG(("Start"));
1716 (!server_entry->server_name ||
1717 !silc_utf8_strcasecmp(server_entry->server_name, server_name))) {
1719 silc_idcache_del_by_context(conn->internal->server_cache,
1720 server_entry, NULL);
1721 silc_free(server_entry->server_name);
1722 server_entry->server_name = strdup(server_name);
1724 /* Normalize server name */
1726 server_namec = silc_identifier_check(server_name, strlen(server_name),
1727 SILC_STRING_UTF8, 256, NULL);
1731 silc_idcache_add(conn->internal->server_cache, server_namec,
1732 &server_entry->id, server_entry);
1737 (!server_entry->server_info ||
1738 !silc_utf8_strcasecmp(server_entry->server_info, server_info))) {
1739 silc_free(server_entry->server_info);
1740 server_entry->server_info = strdup(server_info);
1744 /* Take reference of server entry */
1746 void silc_client_ref_server(SilcClient client, SilcClientConnection conn,
1747 SilcServerEntry server_entry)
1749 silc_atomic_add_int8(&server_entry->internal.refcnt, 1);
1752 /* Release reference of server entry */
1754 void silc_client_unref_server(SilcClient client, SilcClientConnection conn,
1755 SilcServerEntry server_entry)
1758 silc_atomic_sub_int8(&server_entry->internal.refcnt, 1) == 0)
1759 silc_client_del_server(client, conn, server_entry);
1762 /* Free server entry list */
1764 void silc_client_list_free_servers(SilcClient client,
1765 SilcClientConnection conn,
1766 SilcDList server_list)
1768 SilcServerEntry server_entry;
1771 silc_dlist_start(server_list);
1772 while ((server_entry = silc_dlist_get(server_list)))
1773 silc_client_unref_server(client, conn, server_entry);
1775 silc_dlist_uninit(server_list);