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 /* Normalize nickname for search */
82 nicknamec = silc_identifier_check(nickname, strlen(nickname),
83 SILC_STRING_UTF8, 128, NULL);
88 clients = silc_dlist_init();
94 silc_mutex_lock(conn->internal->lock);
97 if (!silc_idcache_find_by_name(conn->internal->client_cache, nicknamec,
99 silc_mutex_unlock(conn->internal->lock);
100 silc_free(nicknamec);
101 silc_dlist_uninit(clients);
106 /* Take all without any further checking */
107 silc_list_start(list);
108 while ((id_cache = silc_list_get(list))) {
109 silc_client_ref_client(client, conn, entry);
110 silc_dlist_add(clients, id_cache->context);
113 /* Check multiple cache entries for exact match */
114 silc_list_start(list);
115 while ((id_cache = silc_list_get(list))) {
116 entry = id_cache->context;
117 if (silc_utf8_strcasecmp(entry->nickname, format)) {
118 silc_client_ref_client(client, conn, entry);
119 silc_dlist_add(clients, entry);
124 silc_mutex_unlock(conn->internal->lock);
126 silc_dlist_start(clients);
128 silc_free(nicknamec);
132 /********************** Client Resolving from Server ************************/
134 /* Resolving context */
137 SilcGetClientCallback completion;
139 } *SilcClientGetClientInternal;
141 /* Resolving command callback */
143 static SilcBool silc_client_get_clients_cb(SilcClient client,
144 SilcClientConnection conn,
151 SilcClientGetClientInternal i = context;
152 SilcClientEntry client_entry;
154 if (error != SILC_STATUS_OK) {
155 SILC_LOG_DEBUG(("Resolving failed: %s", silc_get_status_message(error)));
157 i->completion(client, conn, error, NULL, i->context);
161 /* Add the returned client to list */
163 client_entry = va_arg(ap, SilcClientEntry);
164 silc_client_ref_client(client, conn, client_entry);
165 silc_dlist_add(i->clients, client_entry);
166 client_entry->internal.resolve_cmd_ident = 0;
169 if (status == SILC_STATUS_OK || status == SILC_STATUS_LIST_END) {
170 /* Deliver the clients to the caller */
172 SILC_LOG_DEBUG(("Resolved %d clients", silc_dlist_count(i->clients)));
173 silc_dlist_start(i->clients);
174 i->completion(client, conn, SILC_STATUS_OK, i->clients, i->context);
182 silc_client_list_free(client, conn, i->clients);
187 /* Resolves client information from server by the client ID. */
189 void silc_client_get_client_by_id_resolve(SilcClient client,
190 SilcClientConnection conn,
191 SilcClientID *client_id,
192 SilcBuffer attributes,
193 SilcGetClientCallback completion,
196 SilcClientGetClientInternal i;
197 SilcClientEntry client_entry;
199 SilcUInt16 cmd_ident;
201 if (!client || !conn | !client_id)
204 SILC_LOG_DEBUG(("Resolve client by ID (%s)",
205 silc_id_render(client_id, SILC_ID_CLIENT)));
207 i = silc_calloc(1, sizeof(*i));
210 i->completion = completion;
211 i->context = context;
212 i->clients = silc_dlist_init();
218 /* Attach to resolving, if on going */
219 client_entry = silc_client_get_client_by_id(client, conn, client_id);
220 if (client_entry && client_entry->internal.resolve_cmd_ident) {
221 SILC_LOG_DEBUG(("Attach to existing resolving"));
222 silc_client_unref_client(client, conn, client_entry);
223 silc_client_command_pending(conn, SILC_COMMAND_NONE,
224 client_entry->internal.resolve_cmd_ident,
225 silc_client_get_clients_cb, i);
229 /* Send the command */
230 idp = silc_id_payload_encode(client_id, SILC_ID_CLIENT);
231 cmd_ident = silc_client_command_send(client, conn, SILC_COMMAND_WHOIS,
232 silc_client_get_clients_cb, i,
233 2, 3, silc_buffer_datalen(attributes),
234 4, silc_buffer_datalen(idp));
236 completion(client, conn, SILC_STATUS_ERR_RESOURCE_LIMIT, NULL, context);
238 if (client_entry && cmd_ident)
239 client_entry->internal.resolve_cmd_ident = cmd_ident;
241 silc_client_unref_client(client, conn, client_entry);
242 silc_buffer_free(idp);
245 /* Finds client entry or entries by the `nickname' and `server'. The
246 completion callback will be called when the client entries has been
247 found. Used internally by the library. */
249 static SilcUInt16 silc_client_get_clients_i(SilcClient client,
250 SilcClientConnection conn,
252 const char *nickname,
254 SilcBuffer attributes,
255 SilcGetClientCallback completion,
258 SilcClientGetClientInternal i;
259 char userhost[768 + 1];
262 SILC_LOG_DEBUG(("Resolve client by %s command",
263 silc_get_command_name(command)));
265 if (!client || !conn)
267 if (!nickname && !attributes)
270 i = silc_calloc(1, sizeof(*i));
273 i->clients = silc_dlist_init();
278 i->completion = completion;
279 i->context = context;
281 memset(userhost, 0, sizeof(userhost));
282 if (nickname && server) {
283 len = strlen(nickname) + strlen(server) + 3;
284 silc_strncat(userhost, len, nickname, strlen(nickname));
285 silc_strncat(userhost, len, "@", 1);
286 silc_strncat(userhost, len, server, strlen(server));
287 } else if (nickname) {
288 silc_strncat(userhost, sizeof(userhost) - 1, nickname, strlen(nickname));
291 /* Send the command */
292 if (command == SILC_COMMAND_IDENTIFY)
293 return silc_client_command_send(client, conn, SILC_COMMAND_IDENTIFY,
294 silc_client_get_clients_cb, i,
295 1, 1, userhost, strlen(userhost));
296 return silc_client_command_send(client, conn, SILC_COMMAND_WHOIS,
297 silc_client_get_clients_cb, i,
298 2, 1, userhost, strlen(userhost),
299 3, silc_buffer_datalen(attributes));
302 /* Get clients from server with IDENTIFY command */
304 SilcUInt16 silc_client_get_clients(SilcClient client,
305 SilcClientConnection conn,
306 const char *nickname,
308 SilcGetClientCallback completion,
311 return silc_client_get_clients_i(client, conn, SILC_COMMAND_IDENTIFY,
312 nickname, server, NULL,
313 completion, context);
316 /* Get clients from server with WHOIS command */
318 SilcUInt16 silc_client_get_clients_whois(SilcClient client,
319 SilcClientConnection conn,
320 const char *nickname,
322 SilcBuffer attributes,
323 SilcGetClientCallback completion,
326 return silc_client_get_clients_i(client, conn, SILC_COMMAND_WHOIS,
327 nickname, server, attributes,
328 completion, context);
331 /* ID list resolving context */
333 SilcGetClientCallback completion;
335 SilcBuffer client_id_list;
336 SilcUInt32 list_count;
337 } *GetClientsByListInternal;
339 static SilcBool silc_client_get_clients_list_cb(SilcClient client,
340 SilcClientConnection conn,
347 GetClientsByListInternal i = context;
348 SilcClientEntry client_entry;
354 /* Process the list after all replies have been received */
355 if (status != SILC_STATUS_OK && !SILC_STATUS_IS_ERROR(status) &&
356 status != SILC_STATUS_LIST_END)
359 SILC_LOG_DEBUG(("Resolved all clients"));
361 clients = silc_dlist_init();
363 status = SILC_STATUS_ERR_RESOURCE_LIMIT;
367 for (c = 0; c < i->list_count; c++) {
369 SILC_GET16_MSB(idp_len, i->client_id_list->data + 2);
371 if (!silc_id_payload_parse_id(i->client_id_list->data, idp_len, &id)) {
372 status = SILC_STATUS_ERR_BAD_CLIENT_ID;
376 /* Get client entry */
377 client_entry = silc_client_get_client_by_id(client, conn, &id.u.client_id);
379 silc_dlist_add(clients, client_entry);
381 if (!silc_buffer_pull(i->client_id_list, idp_len)) {
382 status = SILC_STATUS_ERR_BAD_CLIENT_ID;
387 silc_dlist_start(clients);
388 status = SILC_STATUS_OK;
390 i->completion(client, conn, status, clients, i->context);
393 if (status != SILC_STATUS_OK && i->completion)
394 i->completion(client, conn, status, NULL, i->context);
395 silc_client_list_free(client, conn, clients);
400 /* Gets client entries by the list of client ID's `client_id_list'. This
401 always resolves those client ID's it does not know yet from the server
402 so this function might take a while. The `client_id_list' is a list
403 of ID Payloads added one after other. JOIN command reply and USERS
404 command reply for example returns this sort of list. The `completion'
405 will be called after the entries are available. */
407 void silc_client_get_clients_by_list(SilcClient client,
408 SilcClientConnection conn,
409 SilcUInt32 list_count,
410 SilcBuffer client_id_list,
411 SilcGetClientCallback completion,
414 GetClientsByListInternal in;
415 SilcClientEntry entry;
416 unsigned char **res_argv = NULL;
417 SilcUInt32 *res_argv_lens = NULL, *res_argv_types = NULL, res_argc = 0;
422 SILC_LOG_DEBUG(("Resolve clients from Client ID list"));
424 if (!client || !conn || !client_id_list)
427 in = silc_calloc(1, sizeof(*in));
430 in->completion = completion;
431 in->context = context;
432 in->list_count = list_count;
433 in->client_id_list = silc_buffer_copy(client_id_list);
434 if (!in->client_id_list)
437 for (i = 0; i < list_count; i++) {
439 SILC_GET16_MSB(idp_len, client_id_list->data + 2);
441 if (!silc_id_payload_parse_id(client_id_list->data, idp_len, &id))
444 /* Check if we have this client cached already. If we don't have the
445 entry or it has incomplete info, then resolve it from the server. */
446 entry = silc_client_get_client_by_id(client, conn, &id.u.client_id);
447 if (!entry || !entry->nickname[0] || !entry->username[0] ||
450 res_argv = silc_calloc(list_count, sizeof(*res_argv));
451 res_argv_lens = silc_calloc(list_count, sizeof(*res_argv_lens));
452 res_argv_types = silc_calloc(list_count, sizeof(*res_argv_types));
453 if (!res_argv || !res_argv_lens || !res_argv_types) {
454 silc_client_unref_client(client, conn, entry);
459 res_argv[res_argc] = client_id_list->data;
460 res_argv_lens[res_argc] = idp_len;
461 res_argv_types[res_argc] = res_argc + 5;
464 silc_client_unref_client(client, conn, entry);
466 if (!silc_buffer_pull(client_id_list, idp_len))
469 silc_buffer_start(client_id_list);
471 /* Query the unknown client information from server */
473 silc_client_command_send_argv(client, conn, SILC_COMMAND_WHOIS,
474 silc_client_get_clients_list_cb,
475 in, res_argc, res_argv, res_argv_lens,
478 silc_free(res_argv_lens);
479 silc_free(res_argv_types);
483 /* We have the clients in cache, get them and call the completion */
484 silc_client_get_clients_list_cb(client, conn, SILC_COMMAND_WHOIS,
485 SILC_STATUS_OK, SILC_STATUS_OK, in, NULL);
489 silc_buffer_free(in->client_id_list);
492 silc_free(res_argv_lens);
493 silc_free(res_argv_types);
499 SilcClientConnection conn;
500 SilcChannelID channel_id;
501 SilcGetClientCallback completion;
504 } *GetClientsByChannelInternal;
506 SILC_CLIENT_CMD_FUNC(get_clients_by_channel_cb)
508 GetClientsByChannelInternal i = context;
509 SilcClientEntry *clients = NULL;
510 SilcUInt32 clients_count = 0;
511 SilcBool found = FALSE;
512 SilcChannelEntry channel;
513 SilcHashTableList htl;
522 channel = silc_client_get_channel_by_id(i->client, i->conn, &i->channel_id);
523 if (channel && !silc_hash_table_count(channel->user_list)) {
524 clients = silc_calloc(silc_hash_table_count(channel->user_list),
526 silc_hash_table_list(channel->user_list, &htl);
527 while (silc_hash_table_get(&htl, NULL, (void *)&chu))
528 clients[clients_count++] = chu->client;
529 silc_hash_table_list_reset(&htl);
534 i->completion(i->client, i->conn, clients, clients_count, i->context);
537 i->completion(i->client, i->conn, NULL, 0, i->context);
543 /* Gets client entries by the channel entry indicated by `channel'. Thus,
544 it resolves the clients currently on that channel. */
546 void silc_client_get_clients_by_channel(SilcClient client,
547 SilcClientConnection conn,
548 SilcChannelEntry channel,
549 SilcGetClientCallback completion,
552 GetClientsByChannelInternal in;
553 SilcHashTableList htl;
555 SilcClientEntry entry;
556 unsigned char **res_argv = NULL;
557 SilcUInt32 *res_argv_lens = NULL, *res_argv_types = NULL, res_argc = 0;
559 SilcBool wait_res = FALSE;
561 assert(client && conn && channel);
563 SILC_LOG_DEBUG(("Start"));
565 in = silc_calloc(1, sizeof(*in));
568 in->channel_id = *channel->id;
569 in->completion = completion;
570 in->context = context;
572 /* If user list does not exist, send USERS command. */
573 if (!channel->user_list || !silc_hash_table_count(channel->user_list)) {
574 SILC_LOG_DEBUG(("Sending USERS"));
575 silc_client_command_register(client, SILC_COMMAND_USERS, NULL, NULL,
576 silc_client_command_reply_users_i, 0,
578 silc_client_command_send(client, conn, SILC_COMMAND_USERS,
579 conn->cmd_ident, 1, 2, channel->channel_name,
580 strlen(channel->channel_name));
581 silc_client_command_pending(conn, SILC_COMMAND_USERS, conn->cmd_ident,
582 silc_client_command_get_clients_by_channel_cb,
587 silc_hash_table_list(channel->user_list, &htl);
588 while (silc_hash_table_get(&htl, NULL, (void *)&chu)) {
591 /* If the entry has incomplete info, then resolve it from the server. */
592 if (!entry->nickname[0] || !entry->realname) {
593 if (entry->status & SILC_CLIENT_STATUS_RESOLVING) {
594 /* Attach to this resolving and wait until it finishes */
595 silc_client_command_pending(
596 conn, SILC_COMMAND_NONE,
597 entry->resolve_cmd_ident,
598 silc_client_command_get_clients_by_channel_cb,
604 entry->status |= SILC_CLIENT_STATUS_RESOLVING;
605 entry->resolve_cmd_ident = conn->cmd_ident + 1;
607 idp = silc_id_payload_encode(entry->id, SILC_ID_CLIENT);
609 /* No we don't have it, query it from the server. Assemble argument
610 table that will be sent for the WHOIS command later. */
611 res_argv = silc_realloc(res_argv, sizeof(*res_argv) *
613 res_argv_lens = silc_realloc(res_argv_lens, sizeof(*res_argv_lens) *
615 res_argv_types = silc_realloc(res_argv_types, sizeof(*res_argv_types) *
617 res_argv[res_argc] = silc_memdup(idp->data, idp->len);
618 res_argv_lens[res_argc] = idp->len;
619 res_argv_types[res_argc] = res_argc + 4;
622 silc_buffer_free(idp);
625 silc_hash_table_list_reset(&htl);
627 /* Query the client information from server if the list included clients
628 that we don't know about. */
632 /* Send the WHOIS command to server */
633 res_cmd = silc_command_payload_encode(SILC_COMMAND_WHOIS,
634 res_argc, res_argv, res_argv_lens,
635 res_argv_types, ++conn->cmd_ident);
636 silc_client_packet_send(client, conn->sock, SILC_PACKET_COMMAND,
637 NULL, 0, NULL, NULL, res_cmd->data, res_cmd->len,
640 /* Register our own command reply for this command */
641 silc_client_command_register(client, SILC_COMMAND_WHOIS, NULL, NULL,
642 silc_client_command_reply_whois_i, 0,
645 /* Process the applications request after reply has been received */
646 silc_client_command_pending(
647 conn, SILC_COMMAND_WHOIS, conn->cmd_ident,
648 silc_client_command_get_clients_by_channel_cb,
652 silc_buffer_free(res_cmd);
654 silc_free(res_argv_lens);
655 silc_free(res_argv_types);
662 /* We have the clients in cache, get them and call the completion */
663 silc_client_command_get_clients_by_channel_cb((void *)in, NULL);
668 /************************** Client Entry Routines ***************************/
670 /* Creates new client entry and adds it to the ID cache. Returns pointer
673 SilcClientEntry silc_client_add_client(SilcClient client,
674 SilcClientConnection conn,
675 char *nickname, char *username,
676 char *userinfo, SilcClientID *id,
679 SilcClientEntry client_entry;
682 SILC_LOG_DEBUG(("Adding new client entry"));
684 /* Save the client infos */
685 client_entry = silc_calloc(1, sizeof(*client_entry));
689 client_entry->id = *id;
690 client_entry->internal.valid = TRUE;
691 client_entry->mode = mode;
692 client_entry->realname = userinfo ? strdup(userinfo) : NULL;
693 silc_parse_userfqdn(nickname, client_entry->nickname,
694 sizeof(client_entry->nickname),
695 client_entry->server,
696 sizeof(client_entry->server));
697 silc_parse_userfqdn(username, client_entry->username,
698 sizeof(client_entry->username),
699 client_entry->hostname,
700 sizeof(client_entry->hostname));
701 client_entry->channels = silc_hash_table_alloc(1, silc_hash_ptr, NULL, NULL,
702 NULL, NULL, NULL, TRUE);
703 if (!client_entry->channels) {
704 silc_free(client_entry->realname);
705 silc_free(client_entry);
709 /* Normalize nickname */
710 if (client_entry->nickname[0]) {
711 nick = silc_identifier_check(client_entry->nickname,
712 strlen(client_entry->nickname),
713 SILC_STRING_UTF8, 128, NULL);
715 silc_free(client_entry->realname);
716 silc_hash_table_free(client_entry->channels);
717 silc_free(client_entry);
722 /* Format the nickname */
723 silc_client_nickname_format(client, conn, client_entry);
725 silc_mutex_lock(conn->internal->lock);
727 /* Add client to cache, the normalized nickname is saved to cache */
728 if (!silc_idcache_add(conn->internal->client_cache, nick,
729 &client_entry->id, client_entry)) {
731 silc_free(client_entry->realname);
732 silc_hash_table_free(client_entry->channels);
733 silc_free(client_entry);
734 silc_mutex_unlock(conn->internal->lock);
738 client_entry->nickname_normalized = nick;
740 silc_mutex_unlock(conn->internal->lock);
745 /* Updates the `client_entry' with the new information sent as argument. */
747 void silc_client_update_client(SilcClient client,
748 SilcClientConnection conn,
749 SilcClientEntry client_entry,
750 const char *nickname,
751 const char *username,
752 const char *userinfo,
757 SILC_LOG_DEBUG(("Update client entry"));
759 if (!client_entry->realname && userinfo)
760 client_entry->realname = strdup(userinfo);
761 if ((!client_entry->username[0] || !client_entry->hostname[0]) && username)
762 silc_parse_userfqdn(username, client_entry->username,
763 sizeof(client_entry->username),
764 client_entry->hostname,
765 sizeof(client_entry->username));
766 if (!client_entry->nickname[0] && nickname) {
767 silc_parse_userfqdn(nickname, client_entry->nickname,
768 sizeof(client_entry->nickname),
769 client_entry->server,
770 sizeof(client_entry->server));
772 /* Normalize nickname */
773 nick = silc_identifier_check(client_entry->nickname,
774 strlen(client_entry->nickname),
775 SILC_STRING_UTF8, 128, NULL);
779 /* Format nickname */
780 silc_client_nickname_format(client, conn, client_entry);
782 /* Remove the old cache entry and create a new one */
783 silc_idcache_del_by_context(conn->internal->client_cache, client_entry,
785 silc_idcache_add(conn->internal->client_cache, nick, &client_entry->id,
788 client_entry->mode = mode;
791 /* Deletes the client entry and frees all memory. */
793 void silc_client_del_client_entry(SilcClient client,
794 SilcClientConnection conn,
795 SilcClientEntry client_entry)
797 SILC_LOG_DEBUG(("Start"));
799 silc_free(client_entry->realname);
800 if (client_entry->public_key)
801 silc_pkcs_public_key_free(client_entry->public_key);
802 silc_hash_table_free(client_entry->channels);
803 if (client_entry->internal.send_key)
804 silc_cipher_free(client_entry->internal.send_key);
805 if (client_entry->internal.receive_key)
806 silc_cipher_free(client_entry->internal.receive_key);
807 silc_free(client_entry->internal.key);
809 silc_client_ftp_session_free_client(conn, client_entry);
810 if (client_entry->internal->ke)
811 silc_client_abort_key_agreement(client, conn, client_entry);
813 silc_free(client_entry);
816 /* Removes client from the cache by the client entry. */
818 SilcBool silc_client_del_client(SilcClient client, SilcClientConnection conn,
819 SilcClientEntry client_entry)
821 SilcBool ret = silc_idcache_del_by_context(conn->internal->client_cache,
825 /* Remove from channels */
826 silc_client_remove_from_channels(client, conn, client_entry);
828 /* Free the client entry data */
829 silc_client_del_client_entry(client, conn, client_entry);
836 /* Take reference of client entry */
838 void silc_client_ref_client(SilcClient client, SilcClientConnection conn,
839 SilcClientEntry client_entry)
841 silc_atomic_add_int8(&client_entry->internal.refcnt, 1);
844 /* Release reference of client entry */
846 void silc_client_unref_client(SilcClient client, SilcClientConnection conn,
847 SilcClientEntry client_entry)
850 silc_atomic_sub_int8(&client_entry->internal.refcnt, 1) == 0)
851 silc_client_del_client(client, conn, client_entry);
854 /* Free client entry list */
856 void silc_client_list_free(SilcClient client, SilcClientConnection conn,
857 SilcDList client_list)
859 SilcClientEntry client_entry;
862 silc_dlist_start(client_list);
863 while ((client_entry = silc_dlist_get(client_list)))
864 silc_client_unref_client(client, conn, client_entry);
866 silc_dlist_uninit(client_list);
871 /* Formats the nickname of the client specified by the `client_entry'.
872 If the format is specified by the application this will format the
873 nickname and replace the old nickname in the client entry. If the
874 format string is not specified then this function has no effect. */
876 void silc_client_nickname_format(SilcClient client,
877 SilcClientConnection conn,
878 SilcClientEntry client_entry)
881 char newnick[128 + 1];
885 SilcClientEntry entry, unformatted = NULL;
887 SILC_LOG_DEBUG(("Start"));
889 if (!client->internal->params->nickname_format[0])
892 if (!client_entry->nickname[0])
895 /* Get all clients with same nickname. Do not perform the formatting
896 if there aren't any clients with same nickname unless the application
897 is forcing us to do so. */
898 clients = silc_client_get_clients_local(client, conn,
899 client_entry->nickname, NULL);
900 if (!clients && !client->internal->params->nickname_force_format)
905 while ((entry = silc_dlist_get(clients))) {
906 if (entry->internal.valid && entry != client_entry)
908 if (entry->internal.valid && entry != client_entry &&
909 silc_utf8_strcasecmp(entry->nickname, client_entry->nickname)) {
914 if (!len || freebase)
917 /* If we are changing nickname of our local entry we'll enforce
918 that we will always get the unformatted nickname. Give our
919 format number to the one that is not formatted now. */
920 if (unformatted && client_entry == conn->local_entry)
921 client_entry = unformatted;
923 memset(newnick, 0, sizeof(newnick));
924 cp = client->internal->params->nickname_format;
934 if (!client_entry->nickname[0])
936 len = strlen(client_entry->nickname);
937 memcpy(&newnick[off], client_entry->nickname, len);
941 /* Stripped hostname */
942 if (!client_entry->hostname[0])
944 len = strcspn(client_entry->hostname, ".");
945 i = strcspn(client_entry->hostname, "-");
948 memcpy(&newnick[off], client_entry->hostname, len);
953 if (!client_entry->hostname[0])
955 len = strlen(client_entry->hostname);
956 memcpy(&newnick[off], client_entry->hostname, len);
960 /* Stripped server name */
961 if (!client_entry->server)
963 len = strcspn(client_entry->server, ".");
964 memcpy(&newnick[off], client_entry->server, len);
968 /* Full server name */
969 if (!client_entry->server)
971 len = strlen(client_entry->server);
972 memcpy(&newnick[off], client_entry->server, len);
976 /* Ascending number */
981 if (silc_dlist_count(clients) == 1)
984 silc_dlist_start(clients);
985 while ((entry = silc_dlist_get(clients))) {
986 if (!silc_utf8_strncasecmp(entry->nickname, newnick, off))
988 if (strlen(entry->nickname) <= off)
990 num = atoi(&entry->nickname[off]);
995 memset(tmp, 0, sizeof(tmp));
996 snprintf(tmp, sizeof(tmp) - 1, "%d", ++max);
998 memcpy(&newnick[off], tmp, len);
1003 /* Some other character in the string */
1004 memcpy(&newnick[off], cp, 1);
1013 memcpy(client_entry->nickname, newnick, strlen(newnick));
1014 silc_client_list_free(client, conn, clients);
1017 /************************ Channel Searching Locally *************************/
1019 /* Finds entry for channel by the channel name. Returns the entry or NULL
1020 if the entry was not found. It is found only if the client is joined
1023 SilcChannelEntry silc_client_get_channel(SilcClient client,
1024 SilcClientConnection conn,
1027 SilcIDCacheEntry id_cache;
1028 SilcChannelEntry entry;
1030 if (!client || !conn || !channel)
1033 SILC_LOG_DEBUG(("Find channel %s", channel));
1035 /* Normalize name for search */
1036 channel = silc_channel_name_check(channel, strlen(channel), SILC_STRING_UTF8,
1041 silc_mutex_lock(conn->internal->lock);
1043 if (!silc_idcache_find_by_name_one(conn->internal->channel_cache, channel,
1045 silc_mutex_unlock(conn->internal->lock);
1050 SILC_LOG_DEBUG(("Found"));
1052 entry = id_cache->context;
1055 silc_client_ref_channel(client, conn, entry);
1056 silc_mutex_unlock(conn->internal->lock);
1063 /* Finds entry for channel by the channel ID. Returns the entry or NULL
1064 if the entry was not found. It is found only if the client is joined
1067 SilcChannelEntry silc_client_get_channel_by_id(SilcClient client,
1068 SilcClientConnection conn,
1069 SilcChannelID *channel_id)
1071 SilcIDCacheEntry id_cache;
1072 SilcChannelEntry entry;
1074 if (!client || !conn || !channel_id)
1077 SILC_LOG_DEBUG(("Find channel by id %s",
1078 silc_id_render(channel_id, SILC_ID_CHANNEL)));
1080 silc_mutex_lock(conn->internal->lock);
1082 if (!silc_idcache_find_by_id_one(conn->internal->channel_cache, channel_id,
1084 silc_mutex_unlock(conn->internal->lock);
1088 SILC_LOG_DEBUG(("Found"));
1090 entry = id_cache->context;
1093 silc_client_ref_channel(client, conn, entry);
1094 silc_mutex_unlock(conn->internal->lock);
1099 /********************** Channel Resolving from Server ***********************/
1101 /* Channel resolving context */
1104 SilcGetChannelCallback completion;
1106 } *SilcClientGetChannelInternal;
1108 /* Resolving command callback */
1110 static SilcBool silc_client_get_channel_cb(SilcClient client,
1111 SilcClientConnection conn,
1112 SilcCommand command,
1118 SilcClientGetChannelInternal i = context;
1119 SilcChannelEntry entry;
1121 if (error != SILC_STATUS_OK) {
1122 SILC_LOG_DEBUG(("Resolving failed: %s", silc_get_status_message(error)));
1124 i->completion(client, conn, error, NULL, i->context);
1128 /* Add the returned channel to list */
1129 if (i->completion) {
1130 entry = va_arg(ap, SilcChannelEntry);
1131 silc_client_ref_channel(client, conn, entry);
1132 silc_dlist_add(i->channels, entry);
1135 if (status == SILC_STATUS_OK || status == SILC_STATUS_LIST_END) {
1136 /* Deliver the channels to the caller */
1137 if (i->completion) {
1138 SILC_LOG_DEBUG(("Resolved %d channels", silc_dlist_count(i->channels)));
1139 silc_dlist_start(i->channels);
1140 i->completion(client, conn, SILC_STATUS_OK, i->channels, i->context);
1148 silc_client_list_free_channels(client, conn, i->channels);
1153 /* Resolves channel entry from the server by the channel name. */
1155 void silc_client_get_channel_resolve(SilcClient client,
1156 SilcClientConnection conn,
1158 SilcGetChannelCallback completion,
1161 SilcClientGetChannelInternal i;
1163 if (!client || !conn || !channel_name || !completion)
1166 SILC_LOG_DEBUG(("Resolve channel %s", channel_name));
1168 i = silc_calloc(1, sizeof(*i));
1171 i->completion = completion;
1172 i->context = context;
1173 i->channels = silc_dlist_init();
1179 /* Send the command */
1180 if (!silc_client_command_send(client, conn, SILC_COMMAND_IDENTIFY,
1181 silc_client_get_channel_cb, i, 1,
1182 3, channel_name, strlen(channel_name)))
1183 completion(client, conn, SILC_STATUS_ERR_RESOURCE_LIMIT, NULL, context);
1186 /* Resolves channel information from the server by the channel ID. */
1188 void silc_client_get_channel_by_id_resolve(SilcClient client,
1189 SilcClientConnection conn,
1190 SilcChannelID *channel_id,
1191 SilcGetChannelCallback completion,
1194 SilcClientGetChannelInternal i;
1195 SilcChannelEntry channel;
1197 SilcUInt16 cmd_ident;
1199 if (!client || !conn || !channel_id || !completion)
1202 SILC_LOG_DEBUG(("Resolve channel by id %s",
1203 silc_id_render(channel_id, SILC_ID_CHANNEL)));
1205 i = silc_calloc(1, sizeof(*i));
1208 i->completion = completion;
1209 i->context = context;
1210 i->channels = silc_dlist_init();
1216 /* Attach to resolving, if on going */
1217 channel = silc_client_get_channel_by_id(client, conn, channel_id);
1218 if (channel && channel->internal.resolve_cmd_ident) {
1219 SILC_LOG_DEBUG(("Attach to existing resolving"));
1220 silc_client_unref_channel(client, conn, channel);
1221 silc_client_command_pending(conn, SILC_COMMAND_NONE,
1222 channel->internal.resolve_cmd_ident,
1223 silc_client_get_channel_cb, i);
1227 /* Send the command */
1228 idp = silc_id_payload_encode(channel_id, SILC_ID_CHANNEL);
1229 cmd_ident = silc_client_command_send(client, conn, SILC_COMMAND_IDENTIFY,
1230 silc_client_get_channel_cb, i, 1,
1231 5, silc_buffer_datalen(idp));
1232 silc_buffer_free(idp);
1234 completion(client, conn, SILC_STATUS_ERR_RESOURCE_LIMIT, NULL, context);
1236 if (channel && cmd_ident)
1237 channel->internal.resolve_cmd_ident = cmd_ident;
1239 silc_client_unref_channel(client, conn, channel);
1242 /************************* Channel Entry Routines ***************************/
1244 /* Add new channel entry to the ID Cache */
1246 SilcChannelEntry silc_client_add_channel(SilcClient client,
1247 SilcClientConnection conn,
1248 const char *channel_name,
1250 SilcChannelID *channel_id)
1252 SilcChannelEntry channel;
1253 char *channel_namec;
1255 SILC_LOG_DEBUG(("Start"));
1257 channel = silc_calloc(1, sizeof(*channel));
1261 channel->id = *channel_id;
1262 channel->mode = mode;
1264 channel->channel_name = strdup(channel_name);
1265 if (!channel->channel_name) {
1270 channel->user_list = silc_hash_table_alloc(1, silc_hash_ptr, NULL, NULL,
1271 NULL, NULL, NULL, TRUE);
1272 if (!channel->user_list) {
1273 silc_free(channel->channel_name);
1278 /* Normalize channel name */
1279 channel_namec = silc_channel_name_check(channel_name, strlen(channel_name),
1280 SILC_STRING_UTF8, 256, NULL);
1281 if (!channel_namec) {
1282 silc_free(channel->channel_name);
1283 silc_hash_table_free(channel->user_list);
1288 /* Add channel to cache, the normalized channel name is saved to cache */
1289 if (!silc_idcache_add(conn->internal->channel_cache, channel_namec,
1290 &channel->id, channel)) {
1291 silc_free(channel_namec);
1292 silc_free(channel->channel_name);
1293 silc_hash_table_free(channel->user_list);
1301 /* Foreach callbcak to free all users from the channel when deleting a
1304 static void silc_client_del_channel_foreach(void *key, void *context,
1307 SilcChannelUser chu = (SilcChannelUser)context;
1309 SILC_LOG_DEBUG(("Start"));
1311 /* Remove the context from the client's channel hash table as that
1312 table and channel's user_list hash table share this same context. */
1313 silc_hash_table_del(chu->client->channels, chu->channel);
1317 /* Removes channel from the cache by the channel entry. */
1319 SilcBool silc_client_del_channel(SilcClient client, SilcClientConnection conn,
1320 SilcChannelEntry channel)
1322 SilcBool ret = silc_idcache_del_by_context(conn->internal->channel_cache,
1326 SILC_LOG_DEBUG(("Start"));
1328 /* Free all client entrys from the users list. The silc_hash_table_free
1329 will free all the entries so they are not freed at the foreach
1331 silc_hash_table_foreach(channel->user_list, silc_client_del_channel_foreach,
1333 silc_hash_table_free(channel->user_list);
1335 silc_free(channel->channel_name);
1336 silc_free(channel->topic);
1337 if (channel->founder_key)
1338 silc_pkcs_public_key_free(channel->founder_key);
1339 silc_free(channel->key);
1340 if (channel->channel_key)
1341 silc_cipher_free(channel->channel_key);
1343 silc_hmac_free(channel->hmac);
1344 if (channel->old_channel_keys) {
1346 silc_dlist_start(channel->old_channel_keys);
1347 while ((key = silc_dlist_get(channel->old_channel_keys)) != SILC_LIST_END)
1348 silc_cipher_free(key);
1349 silc_dlist_uninit(channel->old_channel_keys);
1351 if (channel->old_hmacs) {
1353 silc_dlist_start(channel->old_hmacs);
1354 while ((hmac = silc_dlist_get(channel->old_hmacs)) != SILC_LIST_END)
1355 silc_hmac_free(hmac);
1356 silc_dlist_uninit(channel->old_hmacs);
1358 silc_schedule_task_del_by_context(conn->client->schedule, channel);
1359 silc_client_del_channel_private_keys(client, conn, channel);
1365 /* Replaces the channel ID of the `channel' to `new_id'. Returns FALSE
1366 if the ID could not be changed. */
1368 SilcBool silc_client_replace_channel_id(SilcClient client,
1369 SilcClientConnection conn,
1370 SilcChannelEntry channel,
1371 SilcChannelID *new_id)
1373 SilcIDCacheEntry id_cache;
1374 SilcBool ret = FALSE;
1379 SILC_LOG_DEBUG(("Old Channel ID id(%s)",
1380 silc_id_render(&channel->id, SILC_ID_CHANNEL)));
1381 SILC_LOG_DEBUG(("New Channel ID id(%s)",
1382 silc_id_render(new_id, SILC_ID_CHANNEL)));
1384 silc_mutex_lock(conn->internal->lock);
1387 if (silc_idcache_find_by_id_one(conn->internal->channel_cache,
1388 &channel->id, &id_cache))
1389 ret = silc_idcache_update(conn->internal->channel_cache, id_cache,
1390 &channel->id, new_id, NULL, NULL, FALSE);
1392 silc_mutex_unlock(conn->internal->lock);
1397 /* Take reference of channel entry */
1399 void silc_client_ref_channel(SilcClient client, SilcClientConnection conn,
1400 SilcChannelEntry channel_entry)
1402 silc_atomic_add_int8(&channel_entry->internal.refcnt, 1);
1405 /* Release reference of channel entry */
1407 void silc_client_unref_channel(SilcClient client, SilcClientConnection conn,
1408 SilcChannelEntry channel_entry)
1410 if (channel_entry &&
1411 silc_atomic_sub_int8(&channel_entry->internal.refcnt, 1) == 0)
1412 silc_client_del_channel(client, conn, channel_entry);
1415 /* Free channel entry list */
1417 void silc_client_list_free_channels(SilcClient client,
1418 SilcClientConnection conn,
1419 SilcDList channel_list)
1421 SilcChannelEntry channel_entry;
1424 silc_dlist_start(channel_list);
1425 while ((channel_entry = silc_dlist_get(channel_list)))
1426 silc_client_unref_channel(client, conn, channel_entry);
1428 silc_dlist_uninit(channel_list);
1432 /************************* Server Searching Locally *************************/
1434 /* Finds entry for server by the server name. */
1436 SilcServerEntry silc_client_get_server(SilcClient client,
1437 SilcClientConnection conn,
1440 SilcIDCacheEntry id_cache;
1441 SilcServerEntry entry;
1443 if (!client || !conn || !server_name)
1446 SILC_LOG_DEBUG(("Find server by name %s", server_name));
1448 /* Normalize server name for search */
1449 server_name = silc_identifier_check(server_name, strlen(server_name),
1450 SILC_STRING_UTF8, 256, NULL);
1454 silc_mutex_lock(conn->internal->lock);
1456 if (!silc_idcache_find_by_name_one(conn->internal->server_cache,
1457 server_name, &id_cache)) {
1458 silc_free(server_name);
1462 SILC_LOG_DEBUG(("Found"));
1465 entry = id_cache->context;
1466 silc_client_ref_server(client, conn, entry);
1468 silc_mutex_unlock(conn->internal->lock);
1470 silc_free(server_name);
1475 /* Finds entry for server by the server ID. */
1477 SilcServerEntry silc_client_get_server_by_id(SilcClient client,
1478 SilcClientConnection conn,
1479 SilcServerID *server_id)
1481 SilcIDCacheEntry id_cache;
1482 SilcServerEntry entry;
1484 if (!client || !conn || !server_id)
1487 SILC_LOG_DEBUG(("Find server by id %s",
1488 silc_id_render(server_id, SILC_ID_SERVER)));
1490 silc_mutex_lock(conn->internal->lock);
1492 if (!silc_idcache_find_by_id_one(conn->internal->server_cache,
1493 (void *)server_id, &id_cache))
1496 SILC_LOG_DEBUG(("Found"));
1499 entry = (SilcServerEntry)id_cache->context;
1500 silc_client_ref_server(client, conn, entry);
1502 silc_mutex_unlock(conn->internal->lock);
1507 /*********************** Server Resolving from Server ***********************/
1509 /* Resolving context */
1512 SilcGetServerCallback completion;
1514 } *SilcClientGetServerInternal;
1516 /* Resolving command callback */
1518 static SilcBool silc_client_get_server_cb(SilcClient client,
1519 SilcClientConnection conn,
1520 SilcCommand command,
1526 SilcClientGetServerInternal i = context;
1527 SilcServerEntry server;
1529 if (error != SILC_STATUS_OK) {
1530 SILC_LOG_DEBUG(("Resolving failed: %s", silc_get_status_message(error)));
1532 i->completion(client, conn, error, NULL, i->context);
1536 /* Add the returned servers to list */
1537 if (i->completion) {
1538 server = va_arg(ap, SilcServerEntry);
1539 silc_client_ref_server(client, conn, server);
1540 silc_dlist_add(i->servers, server);
1541 server->internal.resolve_cmd_ident = 0;
1544 if (status == SILC_STATUS_OK || status == SILC_STATUS_LIST_END) {
1545 /* Deliver the servers to the caller */
1546 if (i->completion) {
1547 SILC_LOG_DEBUG(("Resolved %d servers", silc_dlist_count(i->servers)));
1548 silc_dlist_start(i->servers);
1549 i->completion(client, conn, SILC_STATUS_OK, i->servers, i->context);
1557 silc_client_list_free_servers(client, conn, i->servers);
1562 /* Resolve server by server ID */
1564 void silc_client_get_server_by_id_resolve(SilcClient client,
1565 SilcClientConnection conn,
1566 SilcServerID *server_id,
1567 SilcGetServerCallback completion,
1570 SilcClientGetServerInternal i;
1571 SilcServerEntry server;
1573 SilcUInt16 cmd_ident;
1575 if (!client || !conn || !server_id || !completion)
1578 SILC_LOG_DEBUG(("Resolve server by id %s",
1579 silc_id_render(server_id, SILC_ID_SERVER)));
1581 i = silc_calloc(1, sizeof(*i));
1584 i->completion = completion;
1585 i->context = context;
1586 i->servers = silc_dlist_init();
1592 /* Attach to resolving, if on going */
1593 server = silc_client_get_server_by_id(client, conn, server_id);
1594 if (server && server->internal.resolve_cmd_ident) {
1595 SILC_LOG_DEBUG(("Attach to existing resolving"));
1596 silc_client_unref_server(client, conn, server);
1597 silc_client_command_pending(conn, SILC_COMMAND_NONE,
1598 server->internal.resolve_cmd_ident,
1599 silc_client_get_server_cb, i);
1603 /* Send the command */
1604 idp = silc_id_payload_encode(server_id, SILC_ID_SERVER);
1605 cmd_ident = silc_client_command_send(client, conn, SILC_COMMAND_IDENTIFY,
1606 silc_client_get_server_cb, i, 1,
1607 5, silc_buffer_datalen(idp));
1608 silc_buffer_free(idp);
1610 completion(client, conn, SILC_STATUS_ERR_RESOURCE_LIMIT, NULL, context);
1612 if (server && cmd_ident)
1613 server->internal.resolve_cmd_ident = cmd_ident;
1615 silc_client_unref_server(client, conn, server);
1618 /************************** Server Entry Routines ***************************/
1620 /* Add new server entry */
1622 SilcServerEntry silc_client_add_server(SilcClient client,
1623 SilcClientConnection conn,
1624 const char *server_name,
1625 const char *server_info,
1626 SilcServerID *server_id)
1628 SilcServerEntry server_entry;
1629 char *server_namec = NULL;
1631 SILC_LOG_DEBUG(("Start"));
1633 server_entry = silc_calloc(1, sizeof(*server_entry));
1634 if (!server_entry || !server_id)
1637 server_entry->id = *server_id;
1639 server_entry->server_name = strdup(server_name);
1641 server_entry->server_info = strdup(server_info);
1643 /* Normalize server name */
1645 server_namec = silc_identifier_check(server_name, strlen(server_name),
1646 SILC_STRING_UTF8, 256, NULL);
1647 if (!server_namec) {
1648 silc_free(server_entry->server_name);
1649 silc_free(server_entry->server_info);
1650 silc_free(server_entry);
1655 silc_mutex_lock(conn->internal->lock);
1657 /* Add server to cache */
1658 if (!silc_idcache_add(conn->internal->server_cache, server_namec,
1659 &server_entry->id, server_entry)) {
1660 silc_free(server_namec);
1661 silc_free(server_entry->server_name);
1662 silc_free(server_entry->server_info);
1663 silc_free(server_entry);
1664 silc_mutex_unlock(conn->internal->lock);
1668 silc_mutex_unlock(conn->internal->lock);
1670 return server_entry;
1673 /* Removes server from the cache by the server entry. */
1675 SilcBool silc_client_del_server(SilcClient client, SilcClientConnection conn,
1676 SilcServerEntry server)
1678 SilcBool ret = silc_idcache_del_by_context(conn->internal->server_cache,
1680 silc_free(server->server_name);
1681 silc_free(server->server_info);
1686 /* Updates the `server_entry' with the new information sent as argument. */
1688 void silc_client_update_server(SilcClient client,
1689 SilcClientConnection conn,
1690 SilcServerEntry server_entry,
1691 const char *server_name,
1692 const char *server_info)
1694 char *server_namec = NULL;
1696 SILC_LOG_DEBUG(("Start"));
1699 (!server_entry->server_name ||
1700 !silc_utf8_strcasecmp(server_entry->server_name, server_name))) {
1702 silc_idcache_del_by_context(conn->internal->server_cache,
1703 server_entry, NULL);
1704 silc_free(server_entry->server_name);
1705 server_entry->server_name = strdup(server_name);
1707 /* Normalize server name */
1709 server_namec = silc_identifier_check(server_name, strlen(server_name),
1710 SILC_STRING_UTF8, 256, NULL);
1714 silc_idcache_add(conn->internal->server_cache, server_namec,
1715 &server_entry->id, server_entry);
1720 (!server_entry->server_info ||
1721 !silc_utf8_strcasecmp(server_entry->server_info, server_info))) {
1722 silc_free(server_entry->server_info);
1723 server_entry->server_info = strdup(server_info);
1727 /* Take reference of server entry */
1729 void silc_client_ref_server(SilcClient client, SilcClientConnection conn,
1730 SilcServerEntry server_entry)
1732 silc_atomic_add_int8(&server_entry->internal.refcnt, 1);
1735 /* Release reference of server entry */
1737 void silc_client_unref_server(SilcClient client, SilcClientConnection conn,
1738 SilcServerEntry server_entry)
1741 silc_atomic_sub_int8(&server_entry->internal.refcnt, 1) == 0)
1742 silc_client_del_server(client, conn, server_entry);
1745 /* Free server entry list */
1747 void silc_client_list_free_servers(SilcClient client,
1748 SilcClientConnection conn,
1749 SilcDList server_list)
1751 SilcServerEntry server_entry;
1754 silc_dlist_start(server_list);
1755 while ((server_entry = silc_dlist_get(server_list)))
1756 silc_client_unref_server(client, conn, server_entry);
1758 silc_dlist_uninit(server_list);