X-Git-Url: http://git.silcnet.org/gitweb/?a=blobdiff_plain;f=lib%2Fsilcclient%2Fclient_entry.c;h=a58298fc05f81ebf0610ce48f9455910eb0d097f;hb=9905799a86c606304fd7df2cd401de1740a272a1;hp=b752743351bbe3aebcbcb0ba24e0c54fcd3dfd2f;hpb=e06e6880f43ec8dc6d9b2ec8266f17d1751923ad;p=silc.git diff --git a/lib/silcclient/client_entry.c b/lib/silcclient/client_entry.c index b7527433..a58298fc 100644 --- a/lib/silcclient/client_entry.c +++ b/lib/silcclient/client_entry.c @@ -22,8 +22,6 @@ #include "silcclient.h" #include "client_internal.h" -/* XXX locking */ - /************************ Client Searching Locally **************************/ /* Finds entry for client by the client's ID. Returns the entry or NULL @@ -108,7 +106,7 @@ SilcDList silc_client_get_clients_local(SilcClient client, /* Take all without any further checking */ silc_list_start(list); while ((id_cache = silc_list_get(list))) { - silc_client_ref_client(client, conn, entry); + silc_client_ref_client(client, conn, id_cache->context); silc_dlist_add(clients, id_cache->context); } } else { @@ -397,8 +395,11 @@ static SilcBool silc_client_get_clients_list_cb(SilcClient client, out: if (status != SILC_STATUS_OK && i->completion) i->completion(client, conn, status, NULL, i->context); + silc_client_list_free(client, conn, clients); + silc_buffer_free(i->client_id_list); silc_free(i); + return FALSE; } @@ -422,6 +423,7 @@ SilcUInt16 silc_client_get_clients_by_list(SilcClient client, SilcUInt32 *res_argv_lens = NULL, *res_argv_types = NULL, res_argc = 0; SilcUInt16 idp_len, cmd_ident; SilcID id; + va_list tmp; int i; SILC_LOG_DEBUG(("Resolve clients from Client ID list")); @@ -489,7 +491,7 @@ SilcUInt16 silc_client_get_clients_by_list(SilcClient client, /* We have the clients in cache, get them and call the completion */ silc_client_get_clients_list_cb(client, conn, SILC_COMMAND_WHOIS, - SILC_STATUS_OK, SILC_STATUS_OK, in, NULL); + SILC_STATUS_OK, SILC_STATUS_OK, in, tmp); return 0; err: @@ -729,7 +731,7 @@ SilcClientEntry silc_client_add_client(SilcClient client, } /* Format the nickname */ - silc_client_nickname_format(client, conn, client_entry); + silc_client_nickname_format(client, conn, client_entry, FALSE); silc_mutex_lock(conn->internal->lock); @@ -749,6 +751,8 @@ SilcClientEntry silc_client_add_client(SilcClient client, silc_mutex_unlock(conn->internal->lock); silc_client_ref_client(client, conn, client_entry); + SILC_LOG_DEBUG(("Added %p", client_entry)); + return client_entry; } @@ -787,26 +791,79 @@ void silc_client_update_client(SilcClient client, return; /* Format nickname */ - silc_client_nickname_format(client, conn, client_entry); + silc_client_nickname_format(client, conn, client_entry, + client_entry == conn->local_entry); - /* Remove the old cache entry and create a new one */ - silc_idcache_del_by_context(conn->internal->client_cache, client_entry, - NULL); - silc_idcache_add(conn->internal->client_cache, nick, &client_entry->id, - client_entry); + /* Update cache entry */ + silc_mutex_lock(conn->internal->lock); + silc_idcache_update_by_context(conn->internal->client_cache, + client_entry, NULL, nick, TRUE); + silc_mutex_unlock(conn->internal->lock); + client_entry->nickname_normalized = nick; } client_entry->mode = mode; } +/* Change a client's nickname */ + +SilcBool silc_client_change_nickname(SilcClient client, + SilcClientConnection conn, + SilcClientEntry client_entry, + const char *new_nick, + SilcClientID *new_id, + const unsigned char *idp, + SilcUInt32 idp_len) +{ + char *tmp; + + SILC_LOG_DEBUG(("Change nickname %s to %s", client_entry->nickname, + new_nick)); + + /* Normalize nickname */ + tmp = silc_identifier_check(new_nick, strlen(new_nick), + SILC_STRING_UTF8, 128, NULL); + if (!tmp) + return FALSE; + + /* Update the client entry */ + silc_mutex_lock(conn->internal->lock); + if (!silc_idcache_update_by_context(conn->internal->client_cache, + client_entry, new_id, tmp, TRUE)) { + silc_free(tmp); + silc_mutex_unlock(conn->internal->lock); + return FALSE; + } + silc_mutex_unlock(conn->internal->lock); + + memset(client_entry->nickname, 0, sizeof(client_entry->nickname)); + memcpy(client_entry->nickname, new_nick, strlen(new_nick)); + client_entry->nickname_normalized = tmp; + silc_client_nickname_format(client, conn, client_entry, + client_entry == conn->local_entry); + + /* For my client entry, update ID and set new ID to packet stream */ + if (client_entry == conn->local_entry) { + if (idp && idp_len) { + silc_buffer_enlarge(conn->internal->local_idp, idp_len); + silc_buffer_put(conn->internal->local_idp, idp, idp_len); + } + if (new_id) + silc_packet_set_ids(conn->stream, SILC_ID_CLIENT, conn->local_id, + 0, NULL); + } + + return TRUE; +} + /* Deletes the client entry and frees all memory. */ void silc_client_del_client_entry(SilcClient client, SilcClientConnection conn, SilcClientEntry client_entry) { - SILC_LOG_DEBUG(("Start")); - silc_free(client_entry->realname); + silc_free(client_entry->nickname_normalized); + silc_free(client_entry->internal.key); if (client_entry->public_key) silc_pkcs_public_key_free(client_entry->public_key); silc_hash_table_free(client_entry->channels); @@ -814,12 +871,16 @@ void silc_client_del_client_entry(SilcClient client, silc_cipher_free(client_entry->internal.send_key); if (client_entry->internal.receive_key) silc_cipher_free(client_entry->internal.receive_key); - silc_free(client_entry->internal.key); + if (client_entry->internal.hmac_send) + silc_hmac_free(client_entry->internal.hmac_send); + if (client_entry->internal.hmac_receive) + silc_hmac_free(client_entry->internal.hmac_receive); #if 0 silc_client_ftp_session_free_client(conn, client_entry); if (client_entry->internal->ke) silc_client_abort_key_agreement(client, conn, client_entry); #endif /* 0 */ + silc_atomic_uninit8(&client_entry->internal.refcnt); silc_free(client_entry); } @@ -828,9 +889,21 @@ void silc_client_del_client_entry(SilcClient client, SilcBool silc_client_del_client(SilcClient client, SilcClientConnection conn, SilcClientEntry client_entry) { - SilcBool ret = silc_idcache_del_by_context(conn->internal->client_cache, - client_entry, NULL); -#if 0 + SilcBool ret; + + if (!client_entry) + return FALSE; + + if (silc_atomic_sub_int8(&client_entry->internal.refcnt, 1) > 0) + return FALSE; + + SILC_LOG_DEBUG(("Deleting client %p", client_entry)); + + silc_mutex_lock(conn->internal->lock); + ret = silc_idcache_del_by_context(conn->internal->client_cache, + client_entry, NULL); + silc_mutex_unlock(conn->internal->lock); + if (ret) { /* Remove from channels */ silc_client_remove_from_channels(client, conn, client_entry); @@ -838,7 +911,6 @@ SilcBool silc_client_del_client(SilcClient client, SilcClientConnection conn, /* Free the client entry data */ silc_client_del_client_entry(client, conn, client_entry); } -#endif return ret; } @@ -859,13 +931,12 @@ void silc_client_ref_client(SilcClient client, SilcClientConnection conn, void silc_client_unref_client(SilcClient client, SilcClientConnection conn, SilcClientEntry client_entry) { - if (client_entry) + if (client_entry) { SILC_LOG_DEBUG(("Client %p refcnt %d->%d", client_entry, silc_atomic_get_int8(&client_entry->internal.refcnt), silc_atomic_get_int8(&client_entry->internal.refcnt) - 1)); - if (client_entry && - silc_atomic_sub_int8(&client_entry->internal.refcnt, 1) == 0) silc_client_del_client(client, conn, client_entry); + } } /* Free client entry list */ @@ -884,15 +955,16 @@ void silc_client_list_free(SilcClient client, SilcClientConnection conn, } } - /* Formats the nickname of the client specified by the `client_entry'. If the format is specified by the application this will format the nickname and replace the old nickname in the client entry. If the - format string is not specified then this function has no effect. */ + format string is not specified then this function has no effect. + Returns the client entry that was formatted. */ -void silc_client_nickname_format(SilcClient client, - SilcClientConnection conn, - SilcClientEntry client_entry) +SilcClientEntry silc_client_nickname_format(SilcClient client, + SilcClientConnection conn, + SilcClientEntry client_entry, + SilcBool priority) { char *cp; char newnick[128 + 1]; @@ -901,21 +973,25 @@ void silc_client_nickname_format(SilcClient client, SilcDList clients; SilcClientEntry entry, unformatted = NULL; - SILC_LOG_DEBUG(("Start")); + SILC_LOG_DEBUG(("Format nickname")); if (!client->internal->params->nickname_format[0]) - return; - + return client_entry; if (!client_entry->nickname[0]) - return; + return NULL; /* Get all clients with same nickname. Do not perform the formatting if there aren't any clients with same nickname unless the application is forcing us to do so. */ clients = silc_client_get_clients_local(client, conn, client_entry->nickname, NULL); - if (!clients && !client->internal->params->nickname_force_format) - return; + if (!clients) + return NULL; + if (silc_dlist_count(clients) == 1 && + !client->internal->params->nickname_force_format) { + silc_client_list_free(client, conn, clients); + return client_entry; + } len = 0; freebase = TRUE; @@ -926,15 +1002,16 @@ void silc_client_nickname_format(SilcClient client, silc_utf8_strcasecmp(entry->nickname, client_entry->nickname)) { freebase = FALSE; unformatted = entry; + break; } } - if (!len || freebase) - return; + if (!len || freebase) { + silc_client_list_free(client, conn, clients); + return client_entry; + } - /* If we are changing nickname of our local entry we'll enforce - that we will always get the unformatted nickname. Give our - format number to the one that is not formatted now. */ - if (unformatted && client_entry == conn->local_entry) + /* If priority formatting, this client always gets unformatted nickname. */ + if (unformatted && priority) client_entry = unformatted; memset(newnick, 0, sizeof(newnick)); @@ -1010,7 +1087,7 @@ void silc_client_nickname_format(SilcClient client, } memset(tmp, 0, sizeof(tmp)); - snprintf(tmp, sizeof(tmp) - 1, "%d", ++max); + silc_snprintf(tmp, sizeof(tmp) - 1, "%d", ++max); len = strlen(tmp); memcpy(&newnick[off], tmp, len); off += len; @@ -1029,6 +1106,79 @@ void silc_client_nickname_format(SilcClient client, newnick[off] = 0; memcpy(client_entry->nickname, newnick, strlen(newnick)); silc_client_list_free(client, conn, clients); + + return client_entry; +} + +/* Parses nickname according to nickname format string */ + +SilcBool silc_client_nickname_parse(SilcClient client, + SilcClientConnection conn, + char *nickname, + char **ret_nick) +{ + char *cp, s = 0, e = 0, *nick; + SilcBool n = FALSE; + int len; + + if (!client->internal->params->nickname_format[0]) + return TRUE; + + if (!nickname || !nickname[0]) + return FALSE; + + cp = client->internal->params->nickname_format; + while (cp && *cp) { + if (*cp == '%') { + cp++; + continue; + } + + switch(*cp) { + case 'n': + n = TRUE; + break; + + case 'h': + case 'H': + case 's': + case 'S': + case 'a': + break; + + default: + /* Get separator character */ + if (n) + e = *cp; + else + s = *cp; + break; + } + + cp++; + } + if (!n) + return FALSE; + + /* Parse the nickname */ + nick = nickname; + len = strlen(nick); + if (s) + if (strchr(nickname, s)) + nick = strchr(nickname, s) + 1; + if (e) + if (strchr(nick, e)) + len = strchr(nick, e) - nick; + if (!len) + return FALSE; + + *ret_nick = silc_memdup(nick, len); + if (!(*ret_nick)) + return FALSE; + + SILC_LOG_DEBUG(("Parsed nickname: %s", *ret_nick)); + + return TRUE; } /************************ Channel Searching Locally *************************/ @@ -1263,7 +1413,7 @@ SilcChannelEntry silc_client_add_channel(SilcClient client, if (!channel) return NULL; - silc_atomic_init8(&channel->internal.refcnt, 0); + silc_atomic_init16(&channel->internal.refcnt, 0); channel->id = *channel_id; channel->mode = mode; @@ -1307,70 +1457,66 @@ SilcChannelEntry silc_client_add_channel(SilcClient client, silc_mutex_unlock(conn->internal->lock); silc_client_ref_channel(client, conn, channel); + SILC_LOG_DEBUG(("Added %p", channel)); + return channel; } -/* Foreach callbcak to free all users from the channel when deleting a - channel entry. */ +/* Removes channel from the cache by the channel entry. */ -static void silc_client_del_channel_foreach(void *key, void *context, - void *user_context) +SilcBool silc_client_del_channel(SilcClient client, SilcClientConnection conn, + SilcChannelEntry channel) { - SilcChannelUser chu = (SilcChannelUser)context; + SilcBool ret; + SilcCipher key; + SilcHmac hmac; - SILC_LOG_DEBUG(("Start")); + if (!channel) + return FALSE; - /* Remove the context from the client's channel hash table as that - table and channel's user_list hash table share this same context. */ - silc_hash_table_del(chu->client->channels, chu->channel); - silc_free(chu); -} + if (silc_atomic_sub_int16(&channel->internal.refcnt, 1) > 0) + return FALSE; -/* Removes channel from the cache by the channel entry. */ + SILC_LOG_DEBUG(("Deleting channel %p", channel)); -SilcBool silc_client_del_channel(SilcClient client, SilcClientConnection conn, - SilcChannelEntry channel) -{ - SilcBool ret = silc_idcache_del_by_context(conn->internal->channel_cache, - channel, NULL); -#if 0 + silc_mutex_lock(conn->internal->lock); + ret = silc_idcache_del_by_context(conn->internal->channel_cache, + channel, NULL); + silc_mutex_unlock(conn->internal->lock); - SILC_LOG_DEBUG(("Start")); + if (!ret) + return FALSE; - /* Free all client entrys from the users list. The silc_hash_table_free - will free all the entries so they are not freed at the foreach - callback. */ - silc_hash_table_foreach(channel->user_list, silc_client_del_channel_foreach, - NULL); + silc_client_empty_channel(client, conn, channel); silc_hash_table_free(channel->user_list); - silc_free(channel->channel_name); silc_free(channel->topic); if (channel->founder_key) silc_pkcs_public_key_free(channel->founder_key); - silc_free(channel->key); - if (channel->channel_key) - silc_cipher_free(channel->channel_key); - if (channel->hmac) - silc_hmac_free(channel->hmac); - if (channel->old_channel_keys) { - SilcCipher key; - silc_dlist_start(channel->old_channel_keys); - while ((key = silc_dlist_get(channel->old_channel_keys)) != SILC_LIST_END) + if (channel->internal.channel_key) + silc_cipher_free(channel->internal.channel_key); + if (channel->internal.hmac) + silc_hmac_free(channel->internal.hmac); + if (channel->internal.old_channel_keys) { + silc_dlist_start(channel->internal.old_channel_keys); + while ((key = silc_dlist_get(channel->internal.old_channel_keys))) silc_cipher_free(key); - silc_dlist_uninit(channel->old_channel_keys); + silc_dlist_uninit(channel->internal.old_channel_keys); } - if (channel->old_hmacs) { - SilcHmac hmac; - silc_dlist_start(channel->old_hmacs); - while ((hmac = silc_dlist_get(channel->old_hmacs)) != SILC_LIST_END) + if (channel->internal.old_hmacs) { + silc_dlist_start(channel->internal.old_hmacs); + while ((hmac = silc_dlist_get(channel->internal.old_hmacs))) silc_hmac_free(hmac); - silc_dlist_uninit(channel->old_hmacs); + silc_dlist_uninit(channel->internal.old_hmacs); } - silc_schedule_task_del_by_context(conn->client->schedule, channel); + if (channel->channel_pubkeys) + silc_argument_list_free(channel->channel_pubkeys, + SILC_ARGUMENT_PUBLIC_KEY); silc_client_del_channel_private_keys(client, conn, channel); + silc_atomic_uninit16(&channel->internal.refcnt); + silc_schedule_task_del_by_context(conn->client->schedule, channel); silc_free(channel); -#endif + return ret; } @@ -1382,7 +1528,6 @@ SilcBool silc_client_replace_channel_id(SilcClient client, SilcChannelEntry channel, SilcChannelID *new_id) { - SilcIDCacheEntry id_cache; SilcBool ret = FALSE; if (!new_id) @@ -1393,14 +1538,10 @@ SilcBool silc_client_replace_channel_id(SilcClient client, SILC_LOG_DEBUG(("New Channel ID id(%s)", silc_id_render(new_id, SILC_ID_CHANNEL))); - silc_mutex_lock(conn->internal->lock); - /* Update the ID */ - if (silc_idcache_find_by_id_one(conn->internal->channel_cache, - &channel->id, &id_cache)) - ret = silc_idcache_update(conn->internal->channel_cache, id_cache, - &channel->id, new_id, NULL, NULL, FALSE); - + silc_mutex_lock(conn->internal->lock); + silc_idcache_update_by_context(conn->internal->channel_cache, channel, + new_id, NULL, FALSE); silc_mutex_unlock(conn->internal->lock); return ret; @@ -1411,7 +1552,10 @@ SilcBool silc_client_replace_channel_id(SilcClient client, void silc_client_ref_channel(SilcClient client, SilcClientConnection conn, SilcChannelEntry channel_entry) { - silc_atomic_add_int8(&channel_entry->internal.refcnt, 1); + silc_atomic_add_int16(&channel_entry->internal.refcnt, 1); + SILC_LOG_DEBUG(("Channel %p refcnt %d->%d", channel_entry, + silc_atomic_get_int16(&channel_entry->internal.refcnt) - 1, + silc_atomic_get_int16(&channel_entry->internal.refcnt))); } /* Release reference of channel entry */ @@ -1419,9 +1563,13 @@ void silc_client_ref_channel(SilcClient client, SilcClientConnection conn, void silc_client_unref_channel(SilcClient client, SilcClientConnection conn, SilcChannelEntry channel_entry) { - if (channel_entry && - silc_atomic_sub_int8(&channel_entry->internal.refcnt, 1) == 0) + if (channel_entry) { + SILC_LOG_DEBUG(("Channel %p refcnt %d->%d", channel_entry, + silc_atomic_get_int16(&channel_entry->internal.refcnt), + silc_atomic_get_int16(&channel_entry->internal.refcnt) + - 1)); silc_client_del_channel(client, conn, channel_entry); + } } /* Free channel entry list */ @@ -1468,6 +1616,7 @@ SilcServerEntry silc_client_get_server(SilcClient client, if (!silc_idcache_find_by_name_one(conn->internal->server_cache, server_name, &id_cache)) { silc_free(server_name); + silc_mutex_unlock(conn->internal->lock); return NULL; } @@ -1502,8 +1651,10 @@ SilcServerEntry silc_client_get_server_by_id(SilcClient client, silc_mutex_lock(conn->internal->lock); if (!silc_idcache_find_by_id_one(conn->internal->server_cache, - server_id, &id_cache)) + server_id, &id_cache)) { + silc_mutex_unlock(conn->internal->lock); return NULL; + } SILC_LOG_DEBUG(("Found")); @@ -1643,10 +1794,13 @@ SilcServerEntry silc_client_add_server(SilcClient client, SilcServerEntry server_entry; char *server_namec = NULL; - SILC_LOG_DEBUG(("Start")); + if (!server_id) + return NULL; + + SILC_LOG_DEBUG(("Adding new server %s", server_name)); server_entry = silc_calloc(1, sizeof(*server_entry)); - if (!server_entry || !server_id) + if (!server_entry) return NULL; silc_atomic_init8(&server_entry->internal.refcnt, 0); @@ -1684,6 +1838,8 @@ SilcServerEntry silc_client_add_server(SilcClient client, silc_mutex_unlock(conn->internal->lock); silc_client_ref_server(client, conn, server_entry); + SILC_LOG_DEBUG(("Added %p", server_entry)); + return server_entry; } @@ -1692,11 +1848,28 @@ SilcServerEntry silc_client_add_server(SilcClient client, SilcBool silc_client_del_server(SilcClient client, SilcClientConnection conn, SilcServerEntry server) { - SilcBool ret = silc_idcache_del_by_context(conn->internal->server_cache, - server, NULL); + SilcBool ret; + + if (!server) + return FALSE; + + if (silc_atomic_sub_int8(&server->internal.refcnt, 1) > 0) + return FALSE; + + SILC_LOG_DEBUG(("Deleting server %p", server)); + + silc_mutex_lock(conn->internal->lock); + ret = silc_idcache_del_by_context(conn->internal->server_cache, + server, NULL); + silc_mutex_unlock(conn->internal->lock); + silc_free(server->server_name); silc_free(server->server_info); + if (server->public_key) + silc_pkcs_public_key_free(server->public_key); + silc_atomic_uninit8(&server->internal.refcnt); silc_free(server); + return ret; } @@ -1710,27 +1883,27 @@ void silc_client_update_server(SilcClient client, { char *server_namec = NULL; - SILC_LOG_DEBUG(("Start")); + SILC_LOG_DEBUG(("Updating server %p", server_entry)); if (server_name && (!server_entry->server_name || !silc_utf8_strcasecmp(server_entry->server_name, server_name))) { - silc_idcache_del_by_context(conn->internal->server_cache, - server_entry, NULL); + server_namec = silc_identifier_check(server_name, strlen(server_name), + SILC_STRING_UTF8, 256, NULL); + if (!server_namec) + return; + silc_free(server_entry->server_name); server_entry->server_name = strdup(server_name); + if (!server_entry->server_name) + return; - /* Normalize server name */ - if (server_name) { - server_namec = silc_identifier_check(server_name, strlen(server_name), - SILC_STRING_UTF8, 256, NULL); - if (!server_namec) - return; - - silc_idcache_add(conn->internal->server_cache, server_namec, - &server_entry->id, server_entry); - } + /* Update cache entry */ + silc_mutex_lock(conn->internal->lock); + silc_idcache_update_by_context(conn->internal->server_cache, server_entry, + NULL, server_namec, TRUE); + silc_mutex_unlock(conn->internal->lock); } if (server_info && @@ -1747,6 +1920,9 @@ void silc_client_ref_server(SilcClient client, SilcClientConnection conn, SilcServerEntry server_entry) { silc_atomic_add_int8(&server_entry->internal.refcnt, 1); + SILC_LOG_DEBUG(("Server %p refcnt %d->%d", server_entry, + silc_atomic_get_int8(&server_entry->internal.refcnt) - 1, + silc_atomic_get_int8(&server_entry->internal.refcnt))); } /* Release reference of server entry */ @@ -1754,9 +1930,13 @@ void silc_client_ref_server(SilcClient client, SilcClientConnection conn, void silc_client_unref_server(SilcClient client, SilcClientConnection conn, SilcServerEntry server_entry) { - if (server_entry && - silc_atomic_sub_int8(&server_entry->internal.refcnt, 1) == 0) + if (server_entry) { + SILC_LOG_DEBUG(("Server %p refcnt %d->%d", server_entry, + silc_atomic_get_int8(&server_entry->internal.refcnt), + silc_atomic_get_int8(&server_entry->internal.refcnt) + - 1)); silc_client_del_server(client, conn, server_entry); + } } /* Free server entry list */