5 Author: Pekka Riikonen <priikone@silcnet.org>
7 Copyright (C) 2001 - 2007 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"
25 /************************ Client Searching Locally **************************/
27 /* Finds entry for client by the client's ID. Returns the entry or NULL
28 if the entry was not found. */
30 SilcClientEntry silc_client_get_client_by_id(SilcClient client,
31 SilcClientConnection conn,
32 SilcClientID *client_id)
34 SilcIDCacheEntry id_cache;
35 SilcClientEntry client_entry;
37 if (!client || !conn || !client_id)
40 SILC_LOG_DEBUG(("Finding client by ID (%s)",
41 silc_id_render(client_id, SILC_ID_CLIENT)));
43 silc_mutex_lock(conn->internal->lock);
45 /* Find ID from cache */
46 if (!silc_idcache_find_by_id_one(conn->internal->client_cache, client_id,
48 silc_mutex_unlock(conn->internal->lock);
52 client_entry = id_cache->context;
55 silc_client_ref_client(client, conn, client_entry);
56 silc_mutex_unlock(conn->internal->lock);
58 SILC_LOG_DEBUG(("Found"));
63 /* Finds clients by nickname from local cache. */
65 SilcDList silc_client_get_clients_local_ext(SilcClient client,
66 SilcClientConnection conn,
71 SilcIDCacheEntry id_cache;
74 SilcClientEntry entry;
75 char nick[128 + 1], *nicknamec, *parsed = NULL, *format = NULL;
78 if (!client || !conn || !nickname)
81 /* Get nickname from nickname@server string */
82 silc_parse_userfqdn(nickname, nick, sizeof(nick), server, sizeof(server));
84 /* Parse nickname in case it is formatted */
85 if (!silc_client_nickname_parse(client, conn, (char *)nick, &parsed))
89 format = (char *)nick;
91 SILC_LOG_DEBUG(("Find clients by nickname %s", parsed));
93 /* Normalize nickname for search */
94 nicknamec = silc_identifier_check(parsed, strlen(parsed),
95 SILC_STRING_UTF8, 128, NULL);
101 clients = silc_dlist_init();
103 silc_free(nicknamec);
108 silc_mutex_lock(conn->internal->lock);
110 /* Find from cache */
111 silc_list_init(list, struct SilcIDCacheEntryStruct, next);
112 if (!silc_idcache_find_by_name(conn->internal->client_cache, nicknamec,
114 silc_mutex_unlock(conn->internal->lock);
115 silc_free(nicknamec);
117 silc_dlist_uninit(clients);
120 silc_list_start(list);
123 /* Take all without any further checking */
124 while ((id_cache = silc_list_get(list))) {
125 entry = id_cache->context;
126 if (!get_valid || entry->internal.valid) {
127 silc_client_ref_client(client, conn, id_cache->context);
128 silc_dlist_add(clients, id_cache->context);
132 /* Check multiple cache entries for exact match */
133 while ((id_cache = silc_list_get(list))) {
134 entry = id_cache->context;
136 /* If server was provided, find entries that either have no server
137 set or have the same server. Ignore those that have different
139 if (server[0] && entry->server &&
140 !silc_utf8_strcasecmp(entry->server, server))
143 if (silc_utf8_strcasecmp(entry->nickname,
144 format ? format : parsed) &&
145 (!get_valid || entry->internal.valid)) {
146 silc_client_ref_client(client, conn, entry);
147 silc_dlist_add(clients, entry);
149 /* If format is NULL, we find one exact match with the base
150 nickname (parsed). */
157 silc_mutex_unlock(conn->internal->lock);
159 silc_free(nicknamec);
162 if (!silc_dlist_count(clients)) {
163 silc_dlist_uninit(clients);
167 SILC_LOG_DEBUG(("Found %d clients", silc_dlist_count(clients)));
169 silc_dlist_start(clients);
173 /* Finds clients by nickname from local cache. */
175 SilcDList silc_client_get_clients_local(SilcClient client,
176 SilcClientConnection conn,
177 const char *nickname,
180 return silc_client_get_clients_local_ext(client, conn, nickname, return_all,
184 /********************** Client Resolving from Server ************************/
186 /* Resolving context */
189 SilcGetClientCallback completion;
191 SilcClientEntry client_entry;
192 } *SilcClientGetClientInternal;
194 /* Resolving command callback */
196 static SilcBool silc_client_get_clients_cb(SilcClient client,
197 SilcClientConnection conn,
204 SilcClientGetClientInternal i = context;
205 SilcClientEntry client_entry;
207 if (error != SILC_STATUS_OK) {
208 SILC_LOG_DEBUG(("Resolving failed: %s", silc_get_status_message(error)));
210 if (i->client_entry) {
211 i->client_entry->internal.resolve_cmd_ident = 0;
212 silc_client_unref_client(client, conn, i->client_entry);
216 i->completion(client, conn, error, NULL, i->context);
220 /* Add the returned client to list */
222 client_entry = va_arg(ap, SilcClientEntry);
223 silc_client_ref_client(client, conn, client_entry);
224 silc_dlist_add(i->clients, client_entry);
225 client_entry->internal.resolve_cmd_ident = 0;
228 if (status == SILC_STATUS_OK || status == SILC_STATUS_LIST_END) {
229 /* Deliver the clients to the caller */
231 SILC_LOG_DEBUG(("Resolved %d clients", silc_dlist_count(i->clients)));
233 if (i->client_entry) {
234 i->client_entry->internal.resolve_cmd_ident = 0;
235 silc_client_unref_client(client, conn, i->client_entry);
238 silc_dlist_start(i->clients);
239 i->completion(client, conn, SILC_STATUS_OK, i->clients, i->context);
247 silc_client_list_free(client, conn, i->clients);
252 /* Resolves client information from server by the client ID. */
255 silc_client_get_client_by_id_resolve(SilcClient client,
256 SilcClientConnection conn,
257 SilcClientID *client_id,
258 SilcBuffer attributes,
259 SilcGetClientCallback completion,
262 SilcClientGetClientInternal i;
263 SilcClientEntry client_entry;
265 SilcUInt16 cmd_ident;
267 if (!client || !conn | !client_id) {
268 SILC_LOG_ERROR(("Missing arguments to "
269 "silc_client_get_clients_by_id_resolve call"));
273 SILC_LOG_DEBUG(("Resolve client by ID (%s)",
274 silc_id_render(client_id, SILC_ID_CLIENT)));
276 i = silc_calloc(1, sizeof(*i));
279 i->completion = completion;
280 i->context = context;
281 i->clients = silc_dlist_init();
287 /* Attach to resolving, if on going */
288 client_entry = silc_client_get_client_by_id(client, conn, client_id);
289 if (client_entry && client_entry->internal.resolve_cmd_ident) {
290 SILC_LOG_DEBUG(("Attach to existing resolving"));
291 silc_client_unref_client(client, conn, client_entry);
292 silc_client_command_pending(conn, SILC_COMMAND_NONE,
293 client_entry->internal.resolve_cmd_ident,
294 silc_client_get_clients_cb, i);
295 return client_entry->internal.resolve_cmd_ident;
298 /* Send the command */
299 idp = silc_id_payload_encode(client_id, SILC_ID_CLIENT);
300 cmd_ident = silc_client_command_send(client, conn, SILC_COMMAND_WHOIS,
301 silc_client_get_clients_cb, i,
302 2, 3, silc_buffer_datalen(attributes),
303 4, silc_buffer_datalen(idp));
304 if (!cmd_ident && completion)
305 completion(client, conn, SILC_STATUS_ERR_RESOURCE_LIMIT, NULL, context);
307 if (client_entry && cmd_ident) {
308 client_entry->internal.resolve_cmd_ident = cmd_ident;
309 i->client_entry = client_entry;
311 silc_client_unref_client(client, conn, client_entry);
314 silc_buffer_free(idp);
319 /* Finds client entry or entries by the `nickname' and `server'. The
320 completion callback will be called when the client entries has been
321 found. Used internally by the library. */
323 static SilcUInt16 silc_client_get_clients_i(SilcClient client,
324 SilcClientConnection conn,
326 const char *nickname,
328 SilcBuffer attributes,
329 SilcGetClientCallback completion,
332 SilcClientGetClientInternal i;
333 char nick[128 + 1], serv[256 + 1], userhost[768 + 1], *parsed = NULL;
336 SILC_LOG_DEBUG(("Resolve client by %s command",
337 silc_get_command_name(command)));
339 if (!client || !conn) {
340 SILC_LOG_ERROR(("Missing arguments to silc_client_get_clients call"));
343 if (!nickname && !attributes) {
344 SILC_LOG_ERROR(("Missing arguments to silc_client_get_clients call"));
348 /* Parse server name from the nickname if set */
349 if (silc_parse_userfqdn(nickname, nick, sizeof(nick),
350 serv, sizeof(serv)) == 2)
351 server = (const char *)serv;
352 nickname = (const char *)nick;
354 /* Parse nickname in case it is formatted */
355 if (silc_client_nickname_parse(client, conn, (char *)nickname, &parsed))
356 nickname = (const char *)parsed;
358 i = silc_calloc(1, sizeof(*i));
363 i->clients = silc_dlist_init();
369 i->completion = completion;
370 i->context = context;
372 memset(userhost, 0, sizeof(userhost));
373 if (nickname && server) {
374 len = strlen(nickname) + strlen(server) + 3;
375 silc_strncat(userhost, len, nickname, strlen(nickname));
376 silc_strncat(userhost, len, "@", 1);
377 silc_strncat(userhost, len, server, strlen(server));
378 } else if (nickname) {
379 silc_strncat(userhost, sizeof(userhost) - 1, nickname, strlen(nickname));
383 /* Send the command */
384 if (command == SILC_COMMAND_IDENTIFY)
385 return silc_client_command_send(client, conn, SILC_COMMAND_IDENTIFY,
386 silc_client_get_clients_cb, i,
387 1, 1, userhost, strlen(userhost));
388 return silc_client_command_send(client, conn, SILC_COMMAND_WHOIS,
389 silc_client_get_clients_cb, i,
390 2, 1, userhost, strlen(userhost),
391 3, silc_buffer_datalen(attributes));
394 /* Get clients from server with IDENTIFY command */
396 SilcUInt16 silc_client_get_clients(SilcClient client,
397 SilcClientConnection conn,
398 const char *nickname,
400 SilcGetClientCallback completion,
403 return silc_client_get_clients_i(client, conn, SILC_COMMAND_IDENTIFY,
404 nickname, server, NULL,
405 completion, context);
408 /* Get clients from server with WHOIS command */
410 SilcUInt16 silc_client_get_clients_whois(SilcClient client,
411 SilcClientConnection conn,
412 const char *nickname,
414 SilcBuffer attributes,
415 SilcGetClientCallback completion,
418 return silc_client_get_clients_i(client, conn, SILC_COMMAND_WHOIS,
419 nickname, server, attributes,
420 completion, context);
423 /* ID list resolving context */
425 SilcGetClientCallback completion;
427 SilcBuffer client_id_list;
428 SilcUInt32 list_count;
429 } *GetClientsByListInternal;
431 static SilcBool silc_client_get_clients_list_cb(SilcClient client,
432 SilcClientConnection conn,
439 GetClientsByListInternal i = context;
440 SilcClientEntry client_entry;
446 /* Process the list after all replies have been received */
447 if (status != SILC_STATUS_OK && !SILC_STATUS_IS_ERROR(status) &&
448 status != SILC_STATUS_LIST_END)
451 SILC_LOG_DEBUG(("Resolved all clients"));
453 clients = silc_dlist_init();
455 status = SILC_STATUS_ERR_RESOURCE_LIMIT;
459 for (c = 0; c < i->list_count; c++) {
461 SILC_GET16_MSB(idp_len, i->client_id_list->data + 2);
463 if (!silc_id_payload_parse_id(i->client_id_list->data, idp_len, &id)) {
464 status = SILC_STATUS_ERR_BAD_CLIENT_ID;
468 /* Get client entry */
469 client_entry = silc_client_get_client_by_id(client, conn, &id.u.client_id);
471 silc_dlist_add(clients, client_entry);
473 if (!silc_buffer_pull(i->client_id_list, idp_len)) {
474 status = SILC_STATUS_ERR_BAD_CLIENT_ID;
479 silc_dlist_start(clients);
480 status = SILC_STATUS_OK;
482 i->completion(client, conn, status, clients, i->context);
485 if (status != SILC_STATUS_OK && i->completion)
486 i->completion(client, conn, status, NULL, i->context);
488 silc_client_list_free(client, conn, clients);
489 silc_buffer_free(i->client_id_list);
495 /* Gets client entries by the list of client ID's `client_id_list'. This
496 always resolves those client ID's it does not know yet from the server
497 so this function might take a while. The `client_id_list' is a list
498 of ID Payloads added one after other. JOIN command reply and USERS
499 command reply for example returns this sort of list. The `completion'
500 will be called after the entries are available. */
502 SilcUInt16 silc_client_get_clients_by_list(SilcClient client,
503 SilcClientConnection conn,
504 SilcUInt32 list_count,
505 SilcBuffer client_id_list,
506 SilcGetClientCallback completion,
509 GetClientsByListInternal in;
510 SilcClientEntry entry;
511 unsigned char **res_argv = NULL;
512 SilcUInt32 *res_argv_lens = NULL, *res_argv_types = NULL, res_argc = 0;
513 SilcUInt16 idp_len, cmd_ident;
517 SILC_LOG_DEBUG(("Resolve clients from Client ID list"));
519 if (!client || !conn || !client_id_list)
522 in = silc_calloc(1, sizeof(*in));
525 in->completion = completion;
526 in->context = context;
527 in->list_count = list_count;
528 in->client_id_list = silc_buffer_copy(client_id_list);
529 if (!in->client_id_list)
532 for (i = 0; i < list_count; i++) {
534 SILC_GET16_MSB(idp_len, client_id_list->data + 2);
536 if (!silc_id_payload_parse_id(client_id_list->data, idp_len, &id))
539 /* Check if we have this client cached already. If we don't have the
540 entry or it has incomplete info, then resolve it from the server. */
541 entry = silc_client_get_client_by_id(client, conn, &id.u.client_id);
542 if (!entry || !entry->nickname[0] || !entry->username[0] ||
545 res_argv = silc_calloc(list_count, sizeof(*res_argv));
546 res_argv_lens = silc_calloc(list_count, sizeof(*res_argv_lens));
547 res_argv_types = silc_calloc(list_count, sizeof(*res_argv_types));
548 if (!res_argv || !res_argv_lens || !res_argv_types) {
549 silc_client_unref_client(client, conn, entry);
554 res_argv[res_argc] = client_id_list->data;
555 res_argv_lens[res_argc] = idp_len;
556 res_argv_types[res_argc] = res_argc + 4;
559 silc_client_unref_client(client, conn, entry);
561 if (!silc_buffer_pull(client_id_list, idp_len))
564 silc_buffer_start(client_id_list);
566 /* Query the unknown client information from server */
568 cmd_ident = silc_client_command_send_argv(client,
569 conn, SILC_COMMAND_WHOIS,
570 silc_client_get_clients_list_cb,
571 in, res_argc, res_argv,
575 silc_free(res_argv_lens);
576 silc_free(res_argv_types);
580 /* We have the clients in cache, get them and call the completion */
581 silc_client_get_clients_list_cb(client, conn, SILC_COMMAND_WHOIS,
582 SILC_STATUS_OK, SILC_STATUS_OK, in, NULL);
586 silc_buffer_free(in->client_id_list);
589 silc_free(res_argv_lens);
590 silc_free(res_argv_types);
597 SilcClientConnection conn;
598 SilcChannelID channel_id;
599 SilcGetClientCallback completion;
602 } *GetClientsByChannelInternal;
604 SILC_CLIENT_CMD_FUNC(get_clients_by_channel_cb)
606 GetClientsByChannelInternal i = context;
607 SilcClientEntry *clients = NULL;
608 SilcUInt32 clients_count = 0;
609 SilcBool found = FALSE;
610 SilcChannelEntry channel;
611 SilcHashTableList htl;
620 channel = silc_client_get_channel_by_id(i->client, i->conn, &i->channel_id);
621 if (channel && !silc_hash_table_count(channel->user_list)) {
622 clients = silc_calloc(silc_hash_table_count(channel->user_list),
624 silc_hash_table_list(channel->user_list, &htl);
625 while (silc_hash_table_get(&htl, NULL, (void *)&chu))
626 clients[clients_count++] = chu->client;
627 silc_hash_table_list_reset(&htl);
632 i->completion(i->client, i->conn, clients, clients_count, i->context);
635 i->completion(i->client, i->conn, NULL, 0, i->context);
641 /* Gets client entries by the channel entry indicated by `channel'. Thus,
642 it resolves the clients currently on that channel. */
644 void silc_client_get_clients_by_channel(SilcClient client,
645 SilcClientConnection conn,
646 SilcChannelEntry channel,
647 SilcGetClientCallback completion,
650 GetClientsByChannelInternal in;
651 SilcHashTableList htl;
653 SilcClientEntry entry;
654 unsigned char **res_argv = NULL;
655 SilcUInt32 *res_argv_lens = NULL, *res_argv_types = NULL, res_argc = 0;
657 SilcBool wait_res = FALSE;
659 assert(client && conn && channel);
661 SILC_LOG_DEBUG(("Start"));
663 in = silc_calloc(1, sizeof(*in));
666 in->channel_id = *channel->id;
667 in->completion = completion;
668 in->context = context;
670 /* If user list does not exist, send USERS command. */
671 if (!channel->user_list || !silc_hash_table_count(channel->user_list)) {
672 SILC_LOG_DEBUG(("Sending USERS"));
673 silc_client_command_register(client, SILC_COMMAND_USERS, NULL, NULL,
674 silc_client_command_reply_users_i, 0,
676 silc_client_command_send(client, conn, SILC_COMMAND_USERS,
677 conn->cmd_ident, 1, 2, channel->channel_name,
678 strlen(channel->channel_name));
679 silc_client_command_pending(conn, SILC_COMMAND_USERS, conn->cmd_ident,
680 silc_client_command_get_clients_by_channel_cb,
685 silc_hash_table_list(channel->user_list, &htl);
686 while (silc_hash_table_get(&htl, NULL, (void *)&chu)) {
689 /* If the entry has incomplete info, then resolve it from the server. */
690 if (!entry->nickname[0] || !entry->realname) {
691 if (entry->status & SILC_CLIENT_STATUS_RESOLVING) {
692 /* Attach to this resolving and wait until it finishes */
693 silc_client_command_pending(
694 conn, SILC_COMMAND_NONE,
695 entry->resolve_cmd_ident,
696 silc_client_command_get_clients_by_channel_cb,
702 entry->status |= SILC_CLIENT_STATUS_RESOLVING;
703 entry->resolve_cmd_ident = conn->cmd_ident + 1;
705 idp = silc_id_payload_encode(entry->id, SILC_ID_CLIENT);
707 /* No we don't have it, query it from the server. Assemble argument
708 table that will be sent for the WHOIS command later. */
709 res_argv = silc_realloc(res_argv, sizeof(*res_argv) *
711 res_argv_lens = silc_realloc(res_argv_lens, sizeof(*res_argv_lens) *
713 res_argv_types = silc_realloc(res_argv_types, sizeof(*res_argv_types) *
715 res_argv[res_argc] = silc_memdup(idp->data, idp->len);
716 res_argv_lens[res_argc] = idp->len;
717 res_argv_types[res_argc] = res_argc + 4;
720 silc_buffer_free(idp);
723 silc_hash_table_list_reset(&htl);
725 /* Query the client information from server if the list included clients
726 that we don't know about. */
730 /* Send the WHOIS command to server */
731 res_cmd = silc_command_payload_encode(SILC_COMMAND_WHOIS,
732 res_argc, res_argv, res_argv_lens,
733 res_argv_types, ++conn->cmd_ident);
734 silc_client_packet_send(client, conn->sock, SILC_PACKET_COMMAND,
735 NULL, 0, NULL, NULL, res_cmd->data, res_cmd->len,
738 /* Register our own command reply for this command */
739 silc_client_command_register(client, SILC_COMMAND_WHOIS, NULL, NULL,
740 silc_client_command_reply_whois_i, 0,
743 /* Process the applications request after reply has been received */
744 silc_client_command_pending(
745 conn, SILC_COMMAND_WHOIS, conn->cmd_ident,
746 silc_client_command_get_clients_by_channel_cb,
750 silc_buffer_free(res_cmd);
752 silc_free(res_argv_lens);
753 silc_free(res_argv_types);
760 /* We have the clients in cache, get them and call the completion */
761 silc_client_command_get_clients_by_channel_cb((void *)in, NULL);
766 /************************** Client Entry Routines ***************************/
768 /* Creates new client entry and adds it to the ID cache. Returns pointer
771 SilcClientEntry silc_client_add_client(SilcClient client,
772 SilcClientConnection conn,
773 char *nickname, char *username,
774 char *userinfo, SilcClientID *id,
777 SilcClientEntry client_entry;
778 char *nick = NULL, parsed[128 + 1];
780 SILC_LOG_DEBUG(("Adding new client entry"));
782 /* Save the client infos */
783 client_entry = silc_calloc(1, sizeof(*client_entry));
787 silc_rwlock_alloc(&client_entry->internal.lock);
788 silc_atomic_init32(&client_entry->internal.refcnt, 0);
789 silc_atomic_init32(&client_entry->internal.deleted, 1);
790 client_entry->id = *id;
791 client_entry->mode = mode;
792 client_entry->realname = userinfo ? strdup(userinfo) : NULL;
794 silc_parse_userfqdn(nickname, parsed, sizeof(parsed),
795 client_entry->server, sizeof(client_entry->server));
796 if (nickname && client->internal->params->full_nicknames)
797 silc_snprintf(client_entry->nickname, sizeof(client_entry->nickname),
800 silc_snprintf(client_entry->nickname, sizeof(client_entry->nickname),
803 silc_parse_userfqdn(username, client_entry->username,
804 sizeof(client_entry->username),
805 client_entry->hostname,
806 sizeof(client_entry->hostname));
808 client_entry->channels = silc_hash_table_alloc(NULL, 1, silc_hash_ptr, NULL,
809 NULL, NULL, NULL, NULL, TRUE);
810 if (!client_entry->channels) {
811 silc_free(client_entry->realname);
812 silc_free(client_entry);
816 /* Normalize nickname */
817 if (client_entry->nickname[0]) {
818 nick = silc_identifier_check(parsed, strlen(parsed),
819 SILC_STRING_UTF8, 128, NULL);
821 silc_free(client_entry->realname);
822 silc_hash_table_free(client_entry->channels);
823 silc_free(client_entry);
828 silc_mutex_lock(conn->internal->lock);
830 /* Add client to cache, the normalized nickname is saved to cache */
831 if (!silc_idcache_add(conn->internal->client_cache, nick,
832 &client_entry->id, client_entry)) {
834 silc_free(client_entry->realname);
835 silc_hash_table_free(client_entry->channels);
836 silc_free(client_entry);
837 silc_mutex_unlock(conn->internal->lock);
841 client_entry->nickname_normalized = nick;
843 silc_mutex_unlock(conn->internal->lock);
844 silc_client_ref_client(client, conn, client_entry);
846 /* Format the nickname */
847 silc_client_nickname_format(client, conn, client_entry, FALSE);
849 if (client_entry->nickname[0])
850 client_entry->internal.valid = TRUE;
852 SILC_LOG_DEBUG(("Added %p", client_entry));
857 /* Updates the `client_entry' with the new information sent as argument.
858 This handles entry locking internally. */
860 void silc_client_update_client(SilcClient client,
861 SilcClientConnection conn,
862 SilcClientEntry client_entry,
863 const char *nickname,
864 const char *username,
865 const char *userinfo,
868 char *nick = NULL, parsed[128 + 1];
870 SILC_LOG_DEBUG(("Update client entry"));
872 silc_rwlock_wrlock(client_entry->internal.lock);
874 if (!client_entry->realname && userinfo)
875 client_entry->realname = strdup(userinfo);
877 if ((!client_entry->username[0] || !client_entry->hostname[0]) && username)
878 silc_parse_userfqdn(username, client_entry->username,
879 sizeof(client_entry->username),
880 client_entry->hostname,
881 sizeof(client_entry->username));
883 if (!client_entry->nickname[0] && nickname) {
884 silc_parse_userfqdn(nickname, parsed, sizeof(parsed),
885 client_entry->server, sizeof(client_entry->server));
886 if (client->internal->params->full_nicknames)
887 silc_snprintf(client_entry->nickname, sizeof(client_entry->nickname),
890 silc_snprintf(client_entry->nickname, sizeof(client_entry->nickname),
893 /* Normalize nickname */
894 nick = silc_identifier_check(parsed, strlen(parsed),
895 SILC_STRING_UTF8, 128, NULL);
897 silc_rwlock_unlock(client_entry->internal.lock);
901 /* Format nickname */
902 silc_client_nickname_format(client, conn, client_entry,
903 client_entry == conn->local_entry);
905 /* Update cache entry */
906 silc_mutex_lock(conn->internal->lock);
907 silc_idcache_update_by_context(conn->internal->client_cache,
908 client_entry, NULL, nick, TRUE);
909 silc_mutex_unlock(conn->internal->lock);
910 client_entry->nickname_normalized = nick;
911 client_entry->internal.valid = TRUE;
913 client_entry->mode = mode;
915 silc_rwlock_unlock(client_entry->internal.lock);
918 /* Change a client's nickname. Must be called with `client_entry' locked. */
920 SilcBool silc_client_change_nickname(SilcClient client,
921 SilcClientConnection conn,
922 SilcClientEntry client_entry,
923 const char *new_nick,
924 SilcClientID *new_id,
925 const unsigned char *idp,
930 SILC_LOG_DEBUG(("Change nickname %s to %s", client_entry->nickname,
933 /* Normalize nickname */
934 tmp = silc_identifier_check(new_nick, strlen(new_nick),
935 SILC_STRING_UTF8, 128, NULL);
939 /* Update the client entry */
940 silc_mutex_lock(conn->internal->lock);
941 if (!silc_idcache_update_by_context(conn->internal->client_cache,
942 client_entry, new_id, tmp, TRUE)) {
944 silc_mutex_unlock(conn->internal->lock);
947 silc_mutex_unlock(conn->internal->lock);
949 memset(client_entry->nickname, 0, sizeof(client_entry->nickname));
950 memcpy(client_entry->nickname, new_nick, strlen(new_nick));
951 client_entry->nickname_normalized = tmp;
952 silc_client_nickname_format(client, conn, client_entry,
953 client_entry == conn->local_entry);
955 /* For my client entry, update ID and set new ID to packet stream */
956 if (client_entry == conn->local_entry) {
957 if (idp && idp_len) {
958 silc_buffer_enlarge(conn->internal->local_idp, idp_len);
959 silc_buffer_put(conn->internal->local_idp, idp, idp_len);
962 silc_packet_set_ids(conn->stream, SILC_ID_CLIENT, conn->local_id,
966 client_entry->internal.valid = TRUE;
970 /* Deletes the client entry and frees all memory. */
972 void silc_client_del_client_entry(SilcClient client,
973 SilcClientConnection conn,
974 SilcClientEntry client_entry)
976 silc_free(client_entry->realname);
977 silc_free(client_entry->nickname_normalized);
978 silc_free(client_entry->internal.key);
979 if (client_entry->public_key)
980 silc_pkcs_public_key_free(client_entry->public_key);
981 silc_hash_table_free(client_entry->channels);
982 if (client_entry->internal.send_key)
983 silc_cipher_free(client_entry->internal.send_key);
984 if (client_entry->internal.receive_key)
985 silc_cipher_free(client_entry->internal.receive_key);
986 if (client_entry->internal.hmac_send)
987 silc_hmac_free(client_entry->internal.hmac_send);
988 if (client_entry->internal.hmac_receive)
989 silc_hmac_free(client_entry->internal.hmac_receive);
990 silc_client_ftp_session_free_client(client, client_entry);
991 if (client_entry->internal.ke)
992 silc_client_abort_key_agreement(client, conn, client_entry);
993 silc_atomic_uninit32(&client_entry->internal.deleted);
994 silc_atomic_uninit32(&client_entry->internal.refcnt);
995 silc_rwlock_free(client_entry->internal.lock);
996 silc_free(client_entry);
999 /* Removes client from the cache by the client entry. */
1001 SilcBool silc_client_del_client(SilcClient client, SilcClientConnection conn,
1002 SilcClientEntry client_entry)
1007 SILC_LOG_DEBUG(("Marking client entry %p deleted"));
1009 if (silc_atomic_sub_int32(&client_entry->internal.deleted, 1) != 0) {
1010 SILC_LOG_DEBUG(("Client entry %p already marked deleted"));
1014 silc_client_unref_client(client, conn, client_entry);
1018 /* Internal routine used to find client by ID and if not found this creates
1019 new client entry and returns it. */
1021 SilcClientEntry silc_client_get_client(SilcClient client,
1022 SilcClientConnection conn,
1023 SilcClientID *client_id)
1025 SilcClientEntry client_entry;
1027 client_entry = silc_client_get_client_by_id(client, conn, client_id);
1028 if (!client_entry) {
1029 client_entry = silc_client_add_client(client, conn, NULL, NULL, NULL,
1033 silc_client_ref_client(client, conn, client_entry);
1036 return client_entry;
1041 void silc_client_lock_client(SilcClientEntry client_entry)
1043 silc_rwlock_rdlock(client_entry->internal.lock);
1048 void silc_client_unlock_client(SilcClientEntry client_entry)
1050 silc_rwlock_unlock(client_entry->internal.lock);
1053 /* Take reference of client entry */
1055 SilcClientEntry silc_client_ref_client(SilcClient client,
1056 SilcClientConnection conn,
1057 SilcClientEntry client_entry)
1059 silc_atomic_add_int32(&client_entry->internal.refcnt, 1);
1060 SILC_LOG_DEBUG(("Client %p refcnt %d->%d", client_entry,
1061 silc_atomic_get_int32(&client_entry->internal.refcnt) - 1,
1062 silc_atomic_get_int32(&client_entry->internal.refcnt)));
1063 return client_entry;
1066 /* Release reference of client entry */
1068 void silc_client_unref_client(SilcClient client, SilcClientConnection conn,
1069 SilcClientEntry client_entry)
1076 SILC_LOG_DEBUG(("Client %p refcnt %d->%d", client_entry,
1077 silc_atomic_get_int32(&client_entry->internal.refcnt),
1078 silc_atomic_get_int32(&client_entry->internal.refcnt) - 1));
1080 if (silc_atomic_sub_int32(&client_entry->internal.refcnt, 1) > 0)
1083 SILC_LOG_DEBUG(("Deleting client %p (%d)", client_entry,
1084 silc_atomic_get_int32(&client_entry->internal.deleted)));
1086 silc_mutex_lock(conn->internal->lock);
1087 ret = silc_idcache_del_by_context(conn->internal->client_cache,
1088 client_entry, NULL);
1089 silc_mutex_unlock(conn->internal->lock);
1092 /* Remove from channels */
1093 silc_client_remove_from_channels(client, conn, client_entry);
1095 /* Free the client entry data */
1096 silc_client_del_client_entry(client, conn, client_entry);
1100 /* Free client entry list */
1102 void silc_client_list_free(SilcClient client, SilcClientConnection conn,
1103 SilcDList client_list)
1105 SilcClientEntry client_entry;
1108 silc_dlist_start(client_list);
1109 while ((client_entry = silc_dlist_get(client_list)))
1110 silc_client_unref_client(client, conn, client_entry);
1112 silc_dlist_uninit(client_list);
1116 /* Formats the nickname of the client specified by the `client_entry'.
1117 If the format is specified by the application this will format the
1118 nickname and replace the old nickname in the client entry. If the
1119 format string is not specified then this function has no effect.
1120 Returns the client entry that was formatted. */
1122 SilcClientEntry silc_client_nickname_format(SilcClient client,
1123 SilcClientConnection conn,
1124 SilcClientEntry client_entry,
1128 char newnick[128 + 1];
1129 int i, off = 0, len;
1131 SilcClientEntry entry, unformatted = NULL;
1132 SilcBool formatted = FALSE;
1134 if (!client->internal->params->nickname_format[0])
1135 return client_entry;
1136 if (!client_entry->nickname[0])
1139 SILC_LOG_DEBUG(("Format nickname"));
1141 /* Get all clients with same nickname. Do not perform the formatting
1142 if there aren't any clients with same nickname unless the application
1143 is forcing us to do so. */
1144 clients = silc_client_get_clients_local_ext(client, conn,
1145 client_entry->nickname,
1149 if (silc_dlist_count(clients) == 1 && !priority &&
1150 !client->internal->params->nickname_force_format) {
1151 silc_client_list_free(client, conn, clients);
1152 return client_entry;
1155 /* Is the requested client formatted already */
1156 if (client_entry->nickname_normalized &&
1157 !silc_utf8_strcasecmp(client_entry->nickname,
1158 client_entry->nickname_normalized))
1161 if (client->internal->params->nickname_force_format)
1164 /* Find unformatted client entry */
1165 while ((entry = silc_dlist_get(clients))) {
1166 if (!entry->internal.valid)
1168 if (entry == client_entry)
1170 if (silc_utf8_strcasecmp(entry->nickname, entry->nickname_normalized)) {
1171 unformatted = entry;
1176 /* If there are no other unformatted clients and the requested client is
1177 unformatted, just return it. */
1178 if (!unformatted && !formatted) {
1179 silc_client_list_free(client, conn, clients);
1180 return client_entry;
1183 /* If priority formatting then the requested client will get the
1184 unformatted nickname, and the unformatted client will get a new
1185 formatted nickname. */
1188 /* Simply change the client's nickname to unformatted */
1189 if (!silc_client_nickname_parse(client, conn, client_entry->nickname,
1193 silc_snprintf(client_entry->nickname, sizeof(client_entry->nickname),
1199 /* There was no other unformatted client */
1200 silc_client_list_free(client, conn, clients);
1201 return client_entry;
1204 /* Now format the previously unformatted client */
1205 client_entry = unformatted;
1209 /* If already formatted just return it */
1211 silc_client_list_free(client, conn, clients);
1212 return client_entry;
1215 memset(newnick, 0, sizeof(newnick));
1216 cp = client->internal->params->nickname_format;
1226 if (!client_entry->nickname[0])
1228 len = strlen(client_entry->nickname);
1229 memcpy(&newnick[off], client_entry->nickname, len);
1233 /* Stripped hostname */
1234 if (!client_entry->hostname[0])
1236 len = strcspn(client_entry->hostname, ".");
1237 i = strcspn(client_entry->hostname, "-");
1240 memcpy(&newnick[off], client_entry->hostname, len);
1245 if (!client_entry->hostname[0])
1247 len = strlen(client_entry->hostname);
1248 memcpy(&newnick[off], client_entry->hostname, len);
1252 /* Ascending number */
1257 if (silc_dlist_count(clients) == 1)
1260 silc_dlist_start(clients);
1261 while ((entry = silc_dlist_get(clients))) {
1262 if (!silc_utf8_strncasecmp(entry->nickname, newnick, off))
1264 if (strlen(entry->nickname) <= off)
1266 num = atoi(&entry->nickname[off]);
1271 memset(tmp, 0, sizeof(tmp));
1272 silc_snprintf(tmp, sizeof(tmp) - 1, "%d", ++max);
1274 memcpy(&newnick[off], tmp, len);
1279 /* Some other character in the string */
1280 memcpy(&newnick[off], cp, 1);
1289 memset(client_entry->nickname, 0, sizeof(client_entry->nickname));
1290 memcpy(client_entry->nickname, newnick, strlen(newnick));
1291 silc_client_list_free(client, conn, clients);
1293 return client_entry;
1296 /* Parses nickname according to nickname format string */
1298 SilcBool silc_client_nickname_parse(SilcClient client,
1299 SilcClientConnection conn,
1303 char *cp, s = 0, e = 0, *nick;
1307 if (!client->internal->params->nickname_format[0]) {
1308 *ret_nick = silc_strdup(nickname);
1312 if (!nickname || !nickname[0])
1315 cp = client->internal->params->nickname_format;
1333 /* Get separator character */
1346 /* Parse the nickname */
1350 if (strchr(nickname, s))
1351 nick = strchr(nickname, s) + 1;
1353 if (strchr(nick, e))
1354 len = strchr(nick, e) - nick;
1358 *ret_nick = silc_memdup(nick, len);
1362 SILC_LOG_DEBUG(("Parsed nickname: %s", *ret_nick));
1367 /************************ Channel Searching Locally *************************/
1369 /* Finds entry for channel by the channel name. Returns the entry or NULL
1370 if the entry was not found. It is found only if the client is joined
1373 SilcChannelEntry silc_client_get_channel(SilcClient client,
1374 SilcClientConnection conn,
1378 SilcIDCacheEntry id_cache;
1379 SilcChannelEntry entry = NULL;
1380 char chname[256 + 1], server[256 + 1];
1382 if (!client || !conn || !channel)
1385 SILC_LOG_DEBUG(("Find channel %s", channel));
1387 /* Parse server name from channel name */
1388 silc_parse_userfqdn(channel, chname, sizeof(chname), server, sizeof(server));
1390 /* Normalize name for search */
1391 channel = silc_channel_name_check(chname, strlen(chname), SILC_STRING_UTF8,
1396 silc_mutex_lock(conn->internal->lock);
1398 if (!silc_idcache_find_by_name(conn->internal->channel_cache, channel,
1400 silc_mutex_unlock(conn->internal->lock);
1405 /* If server name was specified with channel name, find the correct
1406 channel entry with the server name. There can only be one channel
1407 with same name on same server. */
1408 silc_list_start(list);
1410 while ((id_cache = silc_list_get(list))) {
1411 entry = id_cache->context;
1412 if (!entry->server[0])
1414 if (silc_utf8_strcasecmp(entry->server, server))
1418 /* Get first channel without server name specified or one with our
1419 current server connection name */
1420 while ((id_cache = silc_list_get(list))) {
1421 entry = id_cache->context;
1422 if (!entry->server[0])
1424 if (silc_utf8_strcasecmp(entry->server, conn->remote_host))
1430 silc_mutex_unlock(conn->internal->lock);
1435 SILC_LOG_DEBUG(("Found channel %s%s%s", entry->channel_name,
1436 entry->server[0] ? "@" : "", entry->server));
1439 silc_client_ref_channel(client, conn, entry);
1440 silc_mutex_unlock(conn->internal->lock);
1447 /* Finds entry for channel by the channel ID. Returns the entry or NULL
1448 if the entry was not found. It is found only if the client is joined
1451 SilcChannelEntry silc_client_get_channel_by_id(SilcClient client,
1452 SilcClientConnection conn,
1453 SilcChannelID *channel_id)
1455 SilcIDCacheEntry id_cache;
1456 SilcChannelEntry entry;
1458 if (!client || !conn || !channel_id)
1461 SILC_LOG_DEBUG(("Find channel by id %s",
1462 silc_id_render(channel_id, SILC_ID_CHANNEL)));
1464 silc_mutex_lock(conn->internal->lock);
1466 if (!silc_idcache_find_by_id_one(conn->internal->channel_cache, channel_id,
1468 silc_mutex_unlock(conn->internal->lock);
1472 SILC_LOG_DEBUG(("Found"));
1474 entry = id_cache->context;
1477 silc_client_ref_channel(client, conn, entry);
1478 silc_mutex_unlock(conn->internal->lock);
1483 /********************** Channel Resolving from Server ***********************/
1485 /* Channel resolving context */
1488 SilcGetChannelCallback completion;
1490 } *SilcClientGetChannelInternal;
1492 /* Resolving command callback */
1494 static SilcBool silc_client_get_channel_cb(SilcClient client,
1495 SilcClientConnection conn,
1496 SilcCommand command,
1502 SilcClientGetChannelInternal i = context;
1503 SilcChannelEntry entry;
1505 if (error != SILC_STATUS_OK) {
1506 SILC_LOG_DEBUG(("Resolving failed: %s", silc_get_status_message(error)));
1508 i->completion(client, conn, error, NULL, i->context);
1512 /* Add the returned channel to list */
1513 if (i->completion) {
1514 entry = va_arg(ap, SilcChannelEntry);
1515 silc_client_ref_channel(client, conn, entry);
1516 silc_dlist_add(i->channels, entry);
1519 if (status == SILC_STATUS_OK || status == SILC_STATUS_LIST_END) {
1520 /* Deliver the channels to the caller */
1521 if (i->completion) {
1522 SILC_LOG_DEBUG(("Resolved %d channels", silc_dlist_count(i->channels)));
1523 silc_dlist_start(i->channels);
1524 i->completion(client, conn, SILC_STATUS_OK, i->channels, i->context);
1532 silc_client_list_free_channels(client, conn, i->channels);
1537 /* Resolves channel entry from the server by the channel name. */
1539 void silc_client_get_channel_resolve(SilcClient client,
1540 SilcClientConnection conn,
1542 SilcGetChannelCallback completion,
1545 SilcClientGetChannelInternal i;
1547 if (!client || !conn || !channel_name || !completion)
1550 SILC_LOG_DEBUG(("Resolve channel %s", channel_name));
1552 i = silc_calloc(1, sizeof(*i));
1555 i->completion = completion;
1556 i->context = context;
1557 i->channels = silc_dlist_init();
1563 /* Send the command */
1564 if (!silc_client_command_send(client, conn, SILC_COMMAND_IDENTIFY,
1565 silc_client_get_channel_cb, i, 1,
1566 3, channel_name, strlen(channel_name))) {
1568 completion(client, conn, SILC_STATUS_ERR_RESOURCE_LIMIT, NULL, context);
1572 /* Resolves channel information from the server by the channel ID. */
1575 silc_client_get_channel_by_id_resolve(SilcClient client,
1576 SilcClientConnection conn,
1577 SilcChannelID *channel_id,
1578 SilcGetChannelCallback completion,
1581 SilcClientGetChannelInternal i;
1583 SilcUInt16 cmd_ident;
1585 if (!client || !conn || !channel_id || !completion)
1588 SILC_LOG_DEBUG(("Resolve channel by id %s",
1589 silc_id_render(channel_id, SILC_ID_CHANNEL)));
1591 i = silc_calloc(1, sizeof(*i));
1594 i->completion = completion;
1595 i->context = context;
1596 i->channels = silc_dlist_init();
1602 /* Send the command */
1603 idp = silc_id_payload_encode(channel_id, SILC_ID_CHANNEL);
1604 cmd_ident = silc_client_command_send(client, conn, SILC_COMMAND_IDENTIFY,
1605 silc_client_get_channel_cb, i, 1,
1606 5, silc_buffer_datalen(idp));
1607 silc_buffer_free(idp);
1608 if (!cmd_ident && completion)
1609 completion(client, conn, SILC_STATUS_ERR_RESOURCE_LIMIT, NULL, context);
1614 /************************* Channel Entry Routines ***************************/
1616 /* Add new channel entry to the ID Cache */
1618 SilcChannelEntry silc_client_add_channel(SilcClient client,
1619 SilcClientConnection conn,
1620 const char *channel_name,
1622 SilcChannelID *channel_id)
1624 SilcChannelEntry channel;
1625 char *channel_namec, name[256 + 1];
1627 SILC_LOG_DEBUG(("Adding channel %s", channel_name));
1629 channel = silc_calloc(1, sizeof(*channel));
1633 silc_rwlock_alloc(&channel->internal.lock);
1634 silc_atomic_init32(&channel->internal.refcnt, 0);
1635 silc_atomic_init32(&channel->internal.deleted, 1);
1636 channel->id = *channel_id;
1637 channel->mode = mode;
1639 silc_parse_userfqdn(channel_name, name, sizeof(name),
1640 channel->server, sizeof(channel->server));
1641 if (client->internal->params->full_channel_names)
1642 channel->channel_name = strdup(channel_name);
1644 channel->channel_name = strdup(name);
1646 if (!channel->channel_name) {
1647 silc_rwlock_free(channel->internal.lock);
1648 silc_atomic_uninit32(&channel->internal.refcnt);
1653 channel->user_list = silc_hash_table_alloc(NULL, 1, silc_hash_ptr, NULL, NULL,
1654 NULL, NULL, NULL, TRUE);
1655 if (!channel->user_list) {
1656 silc_rwlock_free(channel->internal.lock);
1657 silc_atomic_uninit32(&channel->internal.refcnt);
1658 silc_free(channel->channel_name);
1663 /* Normalize channel name */
1664 channel_namec = silc_channel_name_check(name, strlen(name),
1665 SILC_STRING_UTF8, 256, NULL);
1666 if (!channel_namec) {
1667 silc_rwlock_free(channel->internal.lock);
1668 silc_atomic_uninit32(&channel->internal.refcnt);
1669 silc_free(channel->channel_name);
1670 silc_hash_table_free(channel->user_list);
1675 silc_mutex_lock(conn->internal->lock);
1677 /* Add channel to cache, the normalized channel name is saved to cache */
1678 if (!silc_idcache_add(conn->internal->channel_cache, channel_namec,
1679 &channel->id, channel)) {
1680 silc_rwlock_free(channel->internal.lock);
1681 silc_atomic_uninit32(&channel->internal.refcnt);
1682 silc_free(channel_namec);
1683 silc_free(channel->channel_name);
1684 silc_hash_table_free(channel->user_list);
1686 silc_mutex_unlock(conn->internal->lock);
1690 silc_mutex_unlock(conn->internal->lock);
1691 silc_client_ref_channel(client, conn, channel);
1693 SILC_LOG_DEBUG(("Added %p", channel));
1698 /* Removes channel from the cache by the channel entry. */
1700 SilcBool silc_client_del_channel(SilcClient client, SilcClientConnection conn,
1701 SilcChannelEntry channel)
1707 SILC_LOG_DEBUG(("Marking channel entry %p deleted"));
1710 if (silc_atomic_sub_int32(&channel->internal.deleted, 1) != 0) {
1711 SILC_LOG_DEBUG(("Channel entry %p already marked deleted"));
1713 silc_client_del_channel_private_keys(client, conn, channel);
1716 silc_client_unref_channel(client, conn, channel);
1720 /* Replaces the channel ID of the `channel' to `new_id'. Returns FALSE
1721 if the ID could not be changed. This handles entry locking internally. */
1723 SilcBool silc_client_replace_channel_id(SilcClient client,
1724 SilcClientConnection conn,
1725 SilcChannelEntry channel,
1726 SilcChannelID *new_id)
1728 SilcBool ret = FALSE;
1733 SILC_LOG_DEBUG(("Old Channel ID id(%s)",
1734 silc_id_render(&channel->id, SILC_ID_CHANNEL)));
1735 SILC_LOG_DEBUG(("New Channel ID id(%s)",
1736 silc_id_render(new_id, SILC_ID_CHANNEL)));
1739 silc_rwlock_wrlock(channel->internal.lock);
1740 silc_mutex_lock(conn->internal->lock);
1741 silc_idcache_update_by_context(conn->internal->channel_cache, channel,
1742 new_id, NULL, FALSE);
1743 silc_mutex_unlock(conn->internal->lock);
1744 silc_rwlock_unlock(channel->internal.lock);
1751 void silc_client_lock_channel(SilcChannelEntry channel_entry)
1753 silc_rwlock_rdlock(channel_entry->internal.lock);
1756 /* Unlock channel */
1758 void silc_client_unlock_channel(SilcChannelEntry channel_entry)
1760 silc_rwlock_unlock(channel_entry->internal.lock);
1763 /* Take reference of channel entry */
1765 SilcChannelEntry silc_client_ref_channel(SilcClient client,
1766 SilcClientConnection conn,
1767 SilcChannelEntry channel_entry)
1769 silc_atomic_add_int32(&channel_entry->internal.refcnt, 1);
1770 SILC_LOG_DEBUG(("Channel %p refcnt %d->%d", channel_entry,
1771 silc_atomic_get_int32(&channel_entry->internal.refcnt) - 1,
1772 silc_atomic_get_int32(&channel_entry->internal.refcnt)));
1773 return channel_entry;
1776 /* Release reference of channel entry */
1778 void silc_client_unref_channel(SilcClient client, SilcClientConnection conn,
1779 SilcChannelEntry channel_entry)
1781 SilcIDCacheEntry id_cache;
1782 SilcBool ret = TRUE;
1790 SILC_LOG_DEBUG(("Channel %p refcnt %d->%d", channel_entry,
1791 silc_atomic_get_int32(&channel_entry->internal.refcnt),
1792 silc_atomic_get_int32(&channel_entry->internal.refcnt)
1795 if (silc_atomic_sub_int32(&channel_entry->internal.refcnt, 1) > 0)
1798 SILC_LOG_DEBUG(("Deleting channel %p", channel_entry));
1800 silc_mutex_lock(conn->internal->lock);
1801 if (silc_idcache_find_by_context(conn->internal->channel_cache, channel_entry,
1803 namec = id_cache->name;
1804 ret = silc_idcache_del_by_context(conn->internal->channel_cache,
1805 channel_entry, NULL);
1808 silc_mutex_unlock(conn->internal->lock);
1813 silc_client_empty_channel(client, conn, channel_entry);
1814 silc_client_del_channel_private_keys(client, conn, channel_entry);
1815 silc_hash_table_free(channel_entry->user_list);
1816 silc_free(channel_entry->channel_name);
1817 silc_free(channel_entry->topic);
1818 if (channel_entry->founder_key)
1819 silc_pkcs_public_key_free(channel_entry->founder_key);
1820 if (channel_entry->internal.send_key)
1821 silc_cipher_free(channel_entry->internal.send_key);
1822 if (channel_entry->internal.receive_key)
1823 silc_cipher_free(channel_entry->internal.receive_key);
1824 if (channel_entry->internal.hmac)
1825 silc_hmac_free(channel_entry->internal.hmac);
1826 if (channel_entry->internal.old_channel_keys) {
1827 silc_dlist_start(channel_entry->internal.old_channel_keys);
1828 while ((key = silc_dlist_get(channel_entry->internal.old_channel_keys)))
1829 silc_cipher_free(key);
1830 silc_dlist_uninit(channel_entry->internal.old_channel_keys);
1832 if (channel_entry->internal.old_hmacs) {
1833 silc_dlist_start(channel_entry->internal.old_hmacs);
1834 while ((hmac = silc_dlist_get(channel_entry->internal.old_hmacs)))
1835 silc_hmac_free(hmac);
1836 silc_dlist_uninit(channel_entry->internal.old_hmacs);
1838 if (channel_entry->channel_pubkeys)
1839 silc_argument_list_free(channel_entry->channel_pubkeys,
1840 SILC_ARGUMENT_PUBLIC_KEY);
1841 silc_atomic_uninit32(&channel_entry->internal.deleted);
1842 silc_atomic_uninit32(&channel_entry->internal.refcnt);
1843 silc_rwlock_free(channel_entry->internal.lock);
1844 silc_schedule_task_del_by_context(conn->client->schedule, channel_entry);
1845 silc_free(channel_entry);
1848 /* Free channel entry list */
1850 void silc_client_list_free_channels(SilcClient client,
1851 SilcClientConnection conn,
1852 SilcDList channel_list)
1854 SilcChannelEntry channel_entry;
1857 silc_dlist_start(channel_list);
1858 while ((channel_entry = silc_dlist_get(channel_list)))
1859 silc_client_unref_channel(client, conn, channel_entry);
1861 silc_dlist_uninit(channel_list);
1865 /************************* Server Searching Locally *************************/
1867 /* Finds entry for server by the server name. */
1869 SilcServerEntry silc_client_get_server(SilcClient client,
1870 SilcClientConnection conn,
1873 SilcIDCacheEntry id_cache;
1874 SilcServerEntry entry;
1876 if (!client || !conn || !server_name)
1879 SILC_LOG_DEBUG(("Find server by name %s", server_name));
1881 /* Normalize server name for search */
1882 server_name = silc_identifier_check(server_name, strlen(server_name),
1883 SILC_STRING_UTF8, 256, NULL);
1887 silc_mutex_lock(conn->internal->lock);
1889 if (!silc_idcache_find_by_name_one(conn->internal->server_cache,
1890 server_name, &id_cache)) {
1891 silc_free(server_name);
1892 silc_mutex_unlock(conn->internal->lock);
1896 SILC_LOG_DEBUG(("Found"));
1899 entry = id_cache->context;
1900 silc_client_ref_server(client, conn, entry);
1902 silc_mutex_unlock(conn->internal->lock);
1904 silc_free(server_name);
1909 /* Finds entry for server by the server ID. */
1911 SilcServerEntry silc_client_get_server_by_id(SilcClient client,
1912 SilcClientConnection conn,
1913 SilcServerID *server_id)
1915 SilcIDCacheEntry id_cache;
1916 SilcServerEntry entry;
1918 if (!client || !conn || !server_id)
1921 SILC_LOG_DEBUG(("Find server by id %s",
1922 silc_id_render(server_id, SILC_ID_SERVER)));
1924 silc_mutex_lock(conn->internal->lock);
1926 if (!silc_idcache_find_by_id_one(conn->internal->server_cache,
1927 server_id, &id_cache)) {
1928 silc_mutex_unlock(conn->internal->lock);
1932 SILC_LOG_DEBUG(("Found"));
1935 entry = id_cache->context;
1936 silc_client_ref_server(client, conn, entry);
1938 silc_mutex_unlock(conn->internal->lock);
1943 /*********************** Server Resolving from Server ***********************/
1945 /* Resolving context */
1948 SilcGetServerCallback completion;
1950 } *SilcClientGetServerInternal;
1952 /* Resolving command callback */
1954 static SilcBool silc_client_get_server_cb(SilcClient client,
1955 SilcClientConnection conn,
1956 SilcCommand command,
1962 SilcClientGetServerInternal i = context;
1963 SilcServerEntry server;
1965 if (error != SILC_STATUS_OK) {
1966 SILC_LOG_DEBUG(("Resolving failed: %s", silc_get_status_message(error)));
1968 i->completion(client, conn, error, NULL, i->context);
1972 /* Add the returned servers to list */
1973 if (i->completion) {
1974 server = va_arg(ap, SilcServerEntry);
1975 silc_client_ref_server(client, conn, server);
1976 silc_dlist_add(i->servers, server);
1977 server->internal.resolve_cmd_ident = 0;
1980 if (status == SILC_STATUS_OK || status == SILC_STATUS_LIST_END) {
1981 /* Deliver the servers to the caller */
1982 if (i->completion) {
1983 SILC_LOG_DEBUG(("Resolved %d servers", silc_dlist_count(i->servers)));
1984 silc_dlist_start(i->servers);
1985 i->completion(client, conn, SILC_STATUS_OK, i->servers, i->context);
1993 silc_client_list_free_servers(client, conn, i->servers);
1998 /* Resolve server by server ID */
2001 silc_client_get_server_by_id_resolve(SilcClient client,
2002 SilcClientConnection conn,
2003 SilcServerID *server_id,
2004 SilcGetServerCallback completion,
2007 SilcClientGetServerInternal i;
2008 SilcServerEntry server;
2010 SilcUInt16 cmd_ident;
2012 if (!client || !conn || !server_id || !completion)
2015 SILC_LOG_DEBUG(("Resolve server by id %s",
2016 silc_id_render(server_id, SILC_ID_SERVER)));
2018 i = silc_calloc(1, sizeof(*i));
2021 i->completion = completion;
2022 i->context = context;
2023 i->servers = silc_dlist_init();
2029 /* Attach to resolving, if on going */
2030 server = silc_client_get_server_by_id(client, conn, server_id);
2031 if (server && server->internal.resolve_cmd_ident) {
2032 SILC_LOG_DEBUG(("Attach to existing resolving"));
2033 silc_client_unref_server(client, conn, server);
2034 silc_client_command_pending(conn, SILC_COMMAND_NONE,
2035 server->internal.resolve_cmd_ident,
2036 silc_client_get_server_cb, i);
2037 return server->internal.resolve_cmd_ident;
2040 /* Send the command */
2041 idp = silc_id_payload_encode(server_id, SILC_ID_SERVER);
2042 cmd_ident = silc_client_command_send(client, conn, SILC_COMMAND_IDENTIFY,
2043 silc_client_get_server_cb, i, 1,
2044 5, silc_buffer_datalen(idp));
2045 silc_buffer_free(idp);
2046 if (!cmd_ident && completion)
2047 completion(client, conn, SILC_STATUS_ERR_RESOURCE_LIMIT, NULL, context);
2049 if (server && cmd_ident)
2050 server->internal.resolve_cmd_ident = cmd_ident;
2052 silc_client_unref_server(client, conn, server);
2057 /************************** Server Entry Routines ***************************/
2059 /* Add new server entry */
2061 SilcServerEntry silc_client_add_server(SilcClient client,
2062 SilcClientConnection conn,
2063 const char *server_name,
2064 const char *server_info,
2065 SilcServerID *server_id)
2067 SilcServerEntry server_entry;
2068 char *server_namec = NULL;
2073 SILC_LOG_DEBUG(("Adding new server %s", server_name));
2075 server_entry = silc_calloc(1, sizeof(*server_entry));
2079 silc_rwlock_alloc(&server_entry->internal.lock);
2080 silc_atomic_init32(&server_entry->internal.refcnt, 0);
2081 server_entry->id = *server_id;
2083 server_entry->server_name = strdup(server_name);
2085 server_entry->server_info = strdup(server_info);
2087 /* Normalize server name */
2089 server_namec = silc_identifier_check(server_name, strlen(server_name),
2090 SILC_STRING_UTF8, 256, NULL);
2091 if (!server_namec) {
2092 silc_free(server_entry->server_name);
2093 silc_free(server_entry->server_info);
2094 silc_free(server_entry);
2099 silc_mutex_lock(conn->internal->lock);
2101 /* Add server to cache */
2102 if (!silc_idcache_add(conn->internal->server_cache, server_namec,
2103 &server_entry->id, server_entry)) {
2104 silc_free(server_namec);
2105 silc_free(server_entry->server_name);
2106 silc_free(server_entry->server_info);
2107 silc_free(server_entry);
2108 silc_mutex_unlock(conn->internal->lock);
2112 silc_mutex_unlock(conn->internal->lock);
2113 silc_client_ref_server(client, conn, server_entry);
2115 SILC_LOG_DEBUG(("Added %p", server_entry));
2117 return server_entry;
2120 /* Removes server from the cache by the server entry. */
2122 SilcBool silc_client_del_server(SilcClient client, SilcClientConnection conn,
2123 SilcServerEntry server)
2125 SilcIDCacheEntry id_cache;
2126 SilcBool ret = TRUE;
2132 if (silc_atomic_sub_int32(&server->internal.refcnt, 1) > 0)
2135 SILC_LOG_DEBUG(("Deleting server %p", server));
2137 silc_mutex_lock(conn->internal->lock);
2138 if (silc_idcache_find_by_context(conn->internal->server_cache, server,
2140 namec = id_cache->name;
2141 ret = silc_idcache_del_by_context(conn->internal->server_cache,
2145 silc_mutex_unlock(conn->internal->lock);
2147 silc_free(server->server_name);
2148 silc_free(server->server_info);
2149 if (server->public_key)
2150 silc_pkcs_public_key_free(server->public_key);
2151 silc_atomic_uninit32(&server->internal.refcnt);
2152 silc_rwlock_free(server->internal.lock);
2158 /* Updates the `server_entry' with the new information sent as argument. */
2160 void silc_client_update_server(SilcClient client,
2161 SilcClientConnection conn,
2162 SilcServerEntry server_entry,
2163 const char *server_name,
2164 const char *server_info)
2166 char *server_namec = NULL;
2168 SILC_LOG_DEBUG(("Updating server %p", server_entry));
2171 (!server_entry->server_name ||
2172 !silc_utf8_strcasecmp(server_entry->server_name, server_name))) {
2174 server_namec = silc_identifier_check(server_name, strlen(server_name),
2175 SILC_STRING_UTF8, 256, NULL);
2179 silc_free(server_entry->server_name);
2180 server_entry->server_name = strdup(server_name);
2181 if (!server_entry->server_name)
2184 /* Update cache entry */
2185 silc_mutex_lock(conn->internal->lock);
2186 silc_idcache_update_by_context(conn->internal->server_cache, server_entry,
2187 NULL, server_namec, TRUE);
2188 silc_mutex_unlock(conn->internal->lock);
2192 (!server_entry->server_info ||
2193 !silc_utf8_strcasecmp(server_entry->server_info, server_info))) {
2194 silc_free(server_entry->server_info);
2195 server_entry->server_info = strdup(server_info);
2201 void silc_client_lock_server(SilcServerEntry server_entry)
2203 silc_rwlock_rdlock(server_entry->internal.lock);
2208 void silc_client_unlock_server(SilcServerEntry server_entry)
2210 silc_rwlock_unlock(server_entry->internal.lock);
2213 /* Take reference of server entry */
2215 SilcServerEntry silc_client_ref_server(SilcClient client,
2216 SilcClientConnection conn,
2217 SilcServerEntry server_entry)
2219 silc_atomic_add_int32(&server_entry->internal.refcnt, 1);
2220 SILC_LOG_DEBUG(("Server %p refcnt %d->%d", server_entry,
2221 silc_atomic_get_int32(&server_entry->internal.refcnt) - 1,
2222 silc_atomic_get_int32(&server_entry->internal.refcnt)));
2223 return server_entry;
2226 /* Release reference of server entry */
2228 void silc_client_unref_server(SilcClient client, SilcClientConnection conn,
2229 SilcServerEntry server_entry)
2232 SILC_LOG_DEBUG(("Server %p refcnt %d->%d", server_entry,
2233 silc_atomic_get_int32(&server_entry->internal.refcnt),
2234 silc_atomic_get_int32(&server_entry->internal.refcnt)
2236 silc_client_del_server(client, conn, server_entry);
2240 /* Free server entry list */
2242 void silc_client_list_free_servers(SilcClient client,
2243 SilcClientConnection conn,
2244 SilcDList server_list)
2246 SilcServerEntry server_entry;
2249 silc_dlist_start(server_list);
2250 while ((server_entry = silc_dlist_get(server_list)))
2251 silc_client_unref_server(client, conn, server_entry);
2253 silc_dlist_uninit(server_list);