X-Git-Url: http://git.silcnet.org/gitweb/?a=blobdiff_plain;f=lib%2Fsilcclient%2Fidlist.c;h=ffbcf29bda3b2b989750853ca57a2b0afc7d0559;hb=017dec75a98209fbef49eb496c2269b0c49e736d;hp=8da74cafafe066ea7e31aebe16ddf6619243a4fe;hpb=2a2db637fb3b961add7baa38b057648e100df046;p=silc.git diff --git a/lib/silcclient/idlist.c b/lib/silcclient/idlist.c index 8da74caf..ffbcf29b 100644 --- a/lib/silcclient/idlist.c +++ b/lib/silcclient/idlist.c @@ -2,9 +2,9 @@ idlist.c - Author: Pekka Riikonen + Author: Pekka Riikonen - Copyright (C) 2000 Pekka Riikonen + Copyright (C) 2001 Pekka Riikonen This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -21,50 +21,387 @@ #include "clientlibincludes.h" -/* Finds client entry from cache by nickname. If the entry is not found - from the cache this function queries it from the server. If `server' - and `num' are defined as well thisk checks the match from multiple - cache entries thus providing support for multiple same nickname - handling. This also ignores case-sensitivity. */ +typedef struct { + SilcClientCommandContext cmd; + SilcGetClientCallback completion; + char *nickname; + char *server; + void *context; + int found; +} *GetClientInternal; + +SILC_CLIENT_CMD_FUNC(get_client_callback) +{ + GetClientInternal i = (GetClientInternal)context; + SilcClientEntry *clients; + uint32 clients_count; + + /* Get the clients */ + clients = silc_client_get_clients_local(i->cmd->client, i->cmd->conn, + i->nickname, i->server, + &clients_count); + if (clients) { + i->completion(i->cmd->client, i->cmd->conn, clients, + clients_count, i->context); + i->found = TRUE; + silc_free(clients); + } +} + +static void silc_client_get_client_destructor(void *context) +{ + GetClientInternal i = (GetClientInternal)context; + + if (i->found == FALSE) + i->completion(i->cmd->client, i->cmd->conn, NULL, 0, i->context); + + silc_client_command_free(i->cmd); + if (i->nickname) + silc_free(i->nickname); + if (i->server) + silc_free(i->server); + silc_free(i); +} + +/* Finds client entry or entries by the `nickname' and `server'. The + completion callback will be called when the client entries has been found. + + Note: this function is always asynchronous and resolves the client + information from the server. Thus, if you already know the client + information then use the silc_client_get_client_by_id function to + get the client entry since this function may be very slow and should + be used only to initially get the client entries. */ + +void silc_client_get_clients(SilcClient client, + SilcClientConnection conn, + const char *nickname, + const char *server, + SilcGetClientCallback completion, + void *context) +{ + char ident[512]; + SilcClientCommandContext ctx; + GetClientInternal i = silc_calloc(1, sizeof(*i)); + + /* No ID found. Do query from the server. The query is done by + sending simple IDENTIFY command to the server. */ + ctx = silc_client_command_alloc(); + ctx->client = client; + ctx->conn = conn; + ctx->command = silc_client_command_find("IDENTIFY"); + memset(ident, 0, sizeof(ident)); + snprintf(ident, sizeof(ident), "IDENTIFY %s", nickname); + silc_parse_command_line(ident, &ctx->argv, &ctx->argv_lens, + &ctx->argv_types, &ctx->argc, 2); + + i->cmd = silc_client_command_dup(ctx); + i->nickname = nickname ? strdup(nickname) : NULL; + i->server = server ? strdup(server) : NULL; + i->completion = completion; + i->context = context; + + /* Call the command */ + ctx->command->cb(ctx, NULL); + + /* Add pending callback */ + silc_client_command_pending(conn, SILC_COMMAND_IDENTIFY, + conn->cmd_ident, + silc_client_get_client_destructor, + silc_client_command_get_client_callback, + (void *)i); +} + +/* Same as silc_client_get_clients function but does not resolve anything + from the server. This checks local cache and returns all matching + clients from the local cache. If none was found this returns NULL. + The `nickname' is the real nickname of the client, and the `format' + is the formatted nickname to find exact match from multiple found + entries. The format must be same as given in the SilcClientParams + structure to the client library. If the `format' is NULL all found + clients by `nickname' are returned. */ + +SilcClientEntry *silc_client_get_clients_local(SilcClient client, + SilcClientConnection conn, + const char *nickname, + const char *format, + uint32 *clients_count) +{ + SilcIDCacheEntry id_cache; + SilcIDCacheList list = NULL; + SilcClientEntry entry, *clients; + int i = 0; + bool found = FALSE; + + /* Find ID from cache */ + if (!silc_idcache_find_by_name(conn->client_cache, (char *)nickname, &list)) + return NULL; + + if (!silc_idcache_list_count(list)) { + silc_idcache_list_free(list); + return NULL; + } + + clients = silc_calloc(silc_idcache_list_count(list), sizeof(*clients)); + *clients_count = silc_idcache_list_count(list); + + if (!format) { + /* Take all without any further checking */ + silc_idcache_list_first(list, &id_cache); + while (id_cache) { + clients[i++] = id_cache->context; + found = TRUE; + if (!silc_idcache_list_next(list, &id_cache)) + break; + } + } else { + /* Check multiple cache entries for match */ + silc_idcache_list_first(list, &id_cache); + while (id_cache) { + entry = (SilcClientEntry)id_cache->context; + if (strcasecmp(entry->nickname, format)) { + if (!silc_idcache_list_next(list, &id_cache)) { + break; + } else { + continue; + } + } + + clients[i++] = id_cache->context; + found = TRUE; + if (!silc_idcache_list_next(list, &id_cache)) + break; + } + } + + if (list) + silc_idcache_list_free(list); + + if (!found) { + *clients_count = 0; + if (clients) + silc_free(clients); + return NULL; + } + + return clients; +} + +typedef struct { + SilcClient client; + SilcClientConnection conn; + uint32 list_count; + SilcBuffer client_id_list; + SilcGetClientCallback completion; + void *context; + int found; +} *GetClientsByListInternal; + +SILC_CLIENT_CMD_FUNC(get_clients_list_callback) +{ + GetClientsByListInternal i = (GetClientsByListInternal)context; + SilcIDCacheEntry id_cache = NULL; + SilcBuffer client_id_list = i->client_id_list; + SilcClientEntry *clients = NULL; + uint32 clients_count = 0; + int c; + + for (c = 0; c < i->list_count; c++) { + uint16 idp_len; + SilcClientID *client_id; + + /* Get Client ID */ + SILC_GET16_MSB(idp_len, client_id_list->data + 2); + idp_len += 4; + client_id = silc_id_payload_parse_id(client_id_list->data, idp_len); + if (!client_id) + continue; + + /* Get the client entry */ + if (silc_idcache_find_by_id_one_ext(i->conn->client_cache, + (void *)client_id, + NULL, NULL, + silc_hash_client_id_compare, NULL, + &id_cache)) { + clients = silc_realloc(clients, sizeof(*clients) * + (clients_count + 1)); + clients[clients_count] = (SilcClientEntry)id_cache->context; + clients_count++; + i->found = TRUE; + } + + silc_free(client_id); + silc_buffer_pull(client_id_list, idp_len); + } + + if (i->found) { + i->completion(i->client, i->conn, clients, clients_count, i->context); + silc_free(clients); + } +} + +static void silc_client_get_clients_list_destructor(void *context) +{ + GetClientsByListInternal i = (GetClientsByListInternal)context; + + if (i->found == FALSE) + i->completion(i->client, i->conn, NULL, 0, i->context); + + if (i->client_id_list) + silc_buffer_free(i->client_id_list); + silc_free(i); +} + +/* Gets client entries by the list of client ID's `client_id_list'. This + always resolves those client ID's it does not know yet from the server + so this function might take a while. The `client_id_list' is a list + of ID Payloads added one after other. JOIN command reply and USERS + command reply for example returns this sort of list. The `completion' + will be called after the entries are available. */ + +void silc_client_get_clients_by_list(SilcClient client, + SilcClientConnection conn, + uint32 list_count, + SilcBuffer client_id_list, + SilcGetClientCallback completion, + void *context) +{ + SilcIDCacheEntry id_cache = NULL; + int i; + unsigned char **res_argv = NULL; + uint32 *res_argv_lens = NULL, *res_argv_types = NULL, res_argc = 0; + GetClientsByListInternal in; + + in = silc_calloc(1, sizeof(*in)); + in->client = client; + in->conn = conn; + in->list_count = list_count; + in->client_id_list = silc_buffer_copy(client_id_list); + in->completion = completion; + in->context = context; + + for (i = 0; i < list_count; i++) { + uint16 idp_len; + SilcClientID *client_id; + SilcClientEntry entry; + + /* Get Client ID */ + SILC_GET16_MSB(idp_len, client_id_list->data + 2); + idp_len += 4; + client_id = silc_id_payload_parse_id(client_id_list->data, idp_len); + if (!client_id) + continue; + + /* Check if we have this client cached already. */ + id_cache = NULL; + silc_idcache_find_by_id_one_ext(conn->client_cache, (void *)client_id, + NULL, NULL, + silc_hash_client_id_compare, NULL, + &id_cache); + + /* If we don't have the entry or it has incomplete info, then resolve + it from the server. */ + entry = id_cache ? (SilcClientEntry)id_cache->context : NULL; + if (!id_cache || !entry->nickname) { + /* No we don't have it, query it from the server. Assemble argument + table that will be sent fr the IDENTIFY command later. */ + res_argv = silc_realloc(res_argv, sizeof(*res_argv) * + (res_argc + 1)); + res_argv_lens = silc_realloc(res_argv_lens, sizeof(*res_argv_lens) * + (res_argc + 1)); + res_argv_types = silc_realloc(res_argv_types, sizeof(*res_argv_types) * + (res_argc + 1)); + res_argv[res_argc] = client_id_list->data; + res_argv_lens[res_argc] = idp_len; + res_argv_types[res_argc] = res_argc + 5; + res_argc++; + } + + silc_free(client_id); + silc_buffer_pull(client_id_list, idp_len); + } + + /* Query the client information from server if the list included clients + that we don't know about. */ + if (res_argc) { + SilcBuffer res_cmd; + + /* Send the IDENTIFY command to server */ + res_cmd = silc_command_payload_encode(SILC_COMMAND_IDENTIFY, + res_argc, res_argv, res_argv_lens, + res_argv_types, ++conn->cmd_ident); + silc_client_packet_send(client, conn->sock, SILC_PACKET_COMMAND, + NULL, 0, NULL, NULL, res_cmd->data, res_cmd->len, + TRUE); + + silc_client_command_pending(conn, SILC_COMMAND_IDENTIFY, + conn->cmd_ident, + silc_client_get_clients_list_destructor, + silc_client_command_get_clients_list_callback, + (void *)in); + + silc_buffer_push(client_id_list, client_id_list->data - + client_id_list->head); + silc_buffer_free(res_cmd); + silc_free(res_argv); + silc_free(res_argv_lens); + silc_free(res_argv_types); + return; + } + + silc_buffer_push(client_id_list, client_id_list->data - + client_id_list->head); + + /* We have the clients in cache, get them and call the completion */ + silc_client_command_get_clients_list_callback((void *)in, NULL); +} + +/* The old style function to find client entry. This is used by the + library internally. If `query' is TRUE then the client information is + requested by the server. The pending command callback must be set + by the caller. */ SilcClientEntry silc_idlist_get_client(SilcClient client, SilcClientConnection conn, - char *nickname, - char *server, - unsigned int num) + const char *nickname, + const char *format, + bool query) { SilcIDCacheEntry id_cache; SilcIDCacheList list = NULL; SilcClientEntry entry = NULL; /* Find ID from cache */ - if (!silc_idcache_find_by_data_loose(conn->client_cache, nickname, &list)) { - SilcClientCommandContext ctx; - char ident[512]; - + if (!silc_idcache_find_by_name(conn->client_cache, (char *)nickname, + &list)) { identify: - SILC_LOG_DEBUG(("Requesting Client ID from server")); - - /* No ID found. Do query from the server. The query is done by - sending simple IDENTIFY command to the server. */ - ctx = silc_client_command_alloc(); - ctx->client = client; - ctx->conn = conn; - ctx->command = silc_client_command_find("IDENTIFY"); - memset(ident, 0, sizeof(ident)); - snprintf(ident, sizeof(ident), "IDENTIFY %s", nickname); - silc_parse_command_line(ident, &ctx->argv, &ctx->argv_lens, - &ctx->argv_types, &ctx->argc, 2); - ctx->command->cb(ctx); - - if (list) - silc_idcache_list_free(list); + if (query) { + char ident[512]; + SilcClientCommandContext ctx; + + SILC_LOG_DEBUG(("Requesting Client ID from server")); + + /* No ID found. Do query from the server. The query is done by + sending simple IDENTIFY command to the server. */ + ctx = silc_client_command_alloc(); + ctx->client = client; + ctx->conn = conn; + ctx->command = silc_client_command_find("IDENTIFY"); + memset(ident, 0, sizeof(ident)); + snprintf(ident, sizeof(ident), "IDENTIFY %s", nickname); + silc_parse_command_line(ident, &ctx->argv, &ctx->argv_lens, + &ctx->argv_types, &ctx->argc, 2); + ctx->command->cb(ctx, NULL); + + if (list) + silc_idcache_list_free(list); + return NULL; + } return NULL; } - if (!server && !num) { + if (!format) { /* Take first found cache entry */ if (!silc_idcache_list_first(list, &id_cache)) goto identify; @@ -73,22 +410,20 @@ SilcClientEntry silc_idlist_get_client(SilcClient client, } else { /* Check multiple cache entries for match */ silc_idcache_list_first(list, &id_cache); - entry = (SilcClientEntry)id_cache->context; - - while (entry) { - if (server && entry->server && - !strncasecmp(server, entry->server, strlen(server))) - break; - - if (num && entry->num == num) - break; + while (id_cache) { + entry = (SilcClientEntry)id_cache->context; - if (!silc_idcache_list_next(list, &id_cache)) { - entry = NULL; - break; + if (strcasecmp(entry->nickname, format)) { + if (!silc_idcache_list_next(list, &id_cache)) { + entry = NULL; + break; + } else { + entry = NULL; + continue; + } } - entry = (SilcClientEntry)id_cache->context; + break; } /* If match weren't found, request it */ @@ -102,9 +437,10 @@ SilcClientEntry silc_idlist_get_client(SilcClient client, return entry; } -/* Finds client entry from cache by Client ID. */ +/* Finds entry for client by the client's ID. Returns the entry or NULL + if the entry was not found. */ -SilcClientEntry silc_idlist_get_client_by_id(SilcClient client, +SilcClientEntry silc_client_get_client_by_id(SilcClient client, SilcClientConnection conn, SilcClientID *client_id) { @@ -114,8 +450,10 @@ SilcClientEntry silc_idlist_get_client_by_id(SilcClient client, silc_id_render(client_id, SILC_ID_CLIENT))); /* Find ID from cache */ - if (!silc_idcache_find_by_id_one(conn->client_cache, client_id, - SILC_ID_CLIENT, &id_cache)) + if (!silc_idcache_find_by_id_one_ext(conn->client_cache, (void *)client_id, + NULL, NULL, + silc_hash_client_id_compare, NULL, + &id_cache)) return NULL; SILC_LOG_DEBUG(("Found")); @@ -123,19 +461,496 @@ SilcClientEntry silc_idlist_get_client_by_id(SilcClient client, return (SilcClientEntry)id_cache->context; } -/* Finds channel entry from ID cache by channel name. */ +typedef struct { + SilcClient client; + SilcClientConnection conn; + SilcClientID *client_id; + SilcGetClientCallback completion; + void *context; + int found; +} *GetClientByIDInternal; + +SILC_CLIENT_CMD_FUNC(get_client_by_id_callback) +{ + GetClientByIDInternal i = (GetClientByIDInternal)context; + SilcClientEntry entry; + + /* Get the client */ + entry = silc_client_get_client_by_id(i->client, i->conn, + i->client_id); + if (entry) { + i->completion(i->client, i->conn, &entry, 1, i->context); + i->found = TRUE; + } +} + +static void silc_client_get_client_by_id_destructor(void *context) +{ + GetClientByIDInternal i = (GetClientByIDInternal)context; + + if (i->found == FALSE) + i->completion(i->client, i->conn, NULL, 0, i->context); + + if (i->client_id) + silc_free(i->client_id); + silc_free(i); +} + +/* Same as above but will always resolve the information from the server. + Use this only if you know that you don't have the entry and the only + thing you know about the client is its ID. */ + +void silc_client_get_client_by_id_resolve(SilcClient client, + SilcClientConnection conn, + SilcClientID *client_id, + SilcGetClientCallback completion, + void *context) +{ + SilcBuffer idp; + GetClientByIDInternal i = silc_calloc(1, sizeof(*i)); + + idp = silc_id_payload_encode(client_id, SILC_ID_CLIENT); + silc_client_send_command(client, conn, SILC_COMMAND_WHOIS, + ++conn->cmd_ident, + 1, 3, idp->data, idp->len); + silc_buffer_free(idp); + + i->client = client; + i->conn = conn; + i->client_id = silc_id_dup(client_id, SILC_ID_CLIENT); + i->completion = completion; + i->context = context; + + /* Add pending callback */ + silc_client_command_pending(conn, SILC_COMMAND_WHOIS, + conn->cmd_ident, + silc_client_get_client_by_id_destructor, + silc_client_command_get_client_by_id_callback, + (void *)i); +} -SilcChannelEntry silc_idlist_get_channel(SilcClient client, +/* Creates new client entry and adds it to the ID cache. Returns pointer + to the new entry. */ + +SilcClientEntry +silc_client_add_client(SilcClient client, SilcClientConnection conn, + char *nickname, char *username, + char *userinfo, SilcClientID *id, uint32 mode) +{ + SilcClientEntry client_entry; + char *nick = NULL; + + /* Save the client infos */ + client_entry = silc_calloc(1, sizeof(*client_entry)); + client_entry->id = id; + silc_parse_userfqdn(nickname, &nick, &client_entry->server); + silc_parse_userfqdn(username, &client_entry->username, + &client_entry->hostname); + if (userinfo) + client_entry->realname = strdup(userinfo); + client_entry->mode = mode; + if (nick) + client_entry->nickname = strdup(nick); + + /* Format the nickname */ + silc_client_nickname_format(client, conn, client_entry); + + /* Add client to cache, the non-formatted nickname is saved to cache */ + if (!silc_idcache_add(conn->client_cache, nick, client_entry->id, + (void *)client_entry, FALSE)) { + silc_free(client_entry->nickname); + silc_free(client_entry->username); + silc_free(client_entry->hostname); + silc_free(client_entry->server); + silc_free(client_entry); + return NULL; + } + + return client_entry; +} + +/* Updates the `client_entry' with the new information sent as argument. */ + +void silc_client_update_client(SilcClient client, + SilcClientConnection conn, + SilcClientEntry client_entry, + const char *nickname, + const char *username, + const char *userinfo, + uint32 mode) +{ + char *nick = NULL; + + if (!client_entry->username && username) + silc_parse_userfqdn(username, &client_entry->username, + &client_entry->hostname); + if (!client_entry->realname && userinfo) + client_entry->realname = strdup(userinfo); + if (!client_entry->nickname && nickname) { + silc_parse_userfqdn(nickname, &nick, &client_entry->server); + client_entry->nickname = strdup(nick); + silc_client_nickname_format(client, conn, client_entry); + } + client_entry->mode = mode; + + if (nick) { + /* Remove the old cache entry and create a new one */ + silc_idcache_del_by_context(conn->client_cache, client_entry); + silc_idcache_add(conn->client_cache, nick, client_entry->id, + client_entry, FALSE); + } +} + +/* Deletes the client entry and frees all memory. */ + +void silc_client_del_client_entry(SilcClient client, + SilcClientEntry client_entry) +{ + silc_free(client_entry->nickname); + silc_free(client_entry->username); + silc_free(client_entry->realname); + silc_free(client_entry->server); + silc_free(client_entry->id); + if (client_entry->send_key) + silc_cipher_free(client_entry->send_key); + if (client_entry->receive_key) + silc_cipher_free(client_entry->receive_key); + silc_free(client_entry->key); + silc_free(client_entry); +} + +/* Removes client from the cache by the client entry. */ + +bool silc_client_del_client(SilcClient client, SilcClientConnection conn, + SilcClientEntry client_entry) +{ + bool ret = silc_idcache_del_by_context(conn->client_cache, client_entry); + silc_client_del_client_entry(client, client_entry); + return ret; +} + +/* Removes channel from the cache by the channel entry. */ + +bool silc_client_del_channel(SilcClient client, SilcClientConnection conn, + SilcChannelEntry channel) +{ + bool ret = silc_idcache_del_by_context(conn->channel_cache, channel); + silc_free(channel->channel_name); + silc_free(channel->id); + silc_free(channel->key); + if (channel->channel_key) + silc_cipher_free(channel->channel_key); + if (channel->hmac) + silc_hmac_free(channel->hmac); + silc_client_del_channel_private_keys(client, conn, channel); + silc_free(channel); + return ret; +} + +/* Finds entry for channel by the channel name. Returns the entry or NULL + if the entry was not found. It is found only if the client is joined + to the channel. */ + +SilcChannelEntry silc_client_get_channel(SilcClient client, SilcClientConnection conn, char *channel) { SilcIDCacheEntry id_cache; SilcChannelEntry entry; - if (!silc_idcache_find_by_data_one(conn->channel_cache, channel, &id_cache)) + if (!silc_idcache_find_by_name_one(conn->channel_cache, channel, + &id_cache)) return NULL; entry = (SilcChannelEntry)id_cache->context; return entry; } + +/* Finds entry for channel by the channel ID. Returns the entry or NULL + if the entry was not found. It is found only if the client is joined + to the channel. */ + +SilcChannelEntry silc_client_get_channel_by_id(SilcClient client, + SilcClientConnection conn, + SilcChannelID *channel_id) +{ + SilcIDCacheEntry id_cache; + SilcChannelEntry entry; + + if (!silc_idcache_find_by_id_one(conn->channel_cache, channel_id, + &id_cache)) + return NULL; + + entry = (SilcChannelEntry)id_cache->context; + + return entry; +} + +typedef struct { + SilcClient client; + SilcClientConnection conn; + SilcChannelID *channel_id; + SilcGetChannelCallback completion; + void *context; + int found; +} *GetChannelByIDInternal; + +SILC_CLIENT_CMD_FUNC(get_channel_by_id_callback) +{ + GetChannelByIDInternal i = (GetChannelByIDInternal)context; + SilcChannelEntry entry; + + /* Get the channel */ + entry = silc_client_get_channel_by_id(i->client, i->conn, + i->channel_id); + if (entry) { + i->completion(i->client, i->conn, &entry, 1, i->context); + i->found = TRUE; + } +} + +static void silc_client_get_channel_by_id_destructor(void *context) +{ + GetChannelByIDInternal i = (GetChannelByIDInternal)context; + + if (i->found == FALSE) + i->completion(i->client, i->conn, NULL, 0, i->context); + + silc_free(i->channel_id); + silc_free(i); +} + +/* Resolves channel information from the server by the channel ID. */ + +void silc_client_get_channel_by_id_resolve(SilcClient client, + SilcClientConnection conn, + SilcChannelID *channel_id, + SilcGetChannelCallback completion, + void *context) +{ + SilcBuffer idp; + GetChannelByIDInternal i = silc_calloc(1, sizeof(*i)); + + idp = silc_id_payload_encode(channel_id, SILC_ID_CHANNEL); + silc_client_send_command(client, conn, SILC_COMMAND_IDENTIFY, + ++conn->cmd_ident, + 1, 5, idp->data, idp->len); + silc_buffer_free(idp); + + i->client = client; + i->conn = conn; + i->channel_id = silc_id_dup(channel_id, SILC_ID_CHANNEL); + i->completion = completion; + i->context = context; + + /* Add pending callback */ + silc_client_command_pending(conn, SILC_COMMAND_IDENTIFY, + conn->cmd_ident, + silc_client_get_channel_by_id_destructor, + silc_client_command_get_channel_by_id_callback, + (void *)i); +} + +/* Find channel entry by ID. This routine is used internally by the library. */ + +SilcChannelEntry silc_idlist_get_channel_by_id(SilcClient client, + SilcClientConnection conn, + SilcChannelID *channel_id, + int query) +{ + SilcBuffer idp; + SilcChannelEntry channel; + + channel = silc_client_get_channel_by_id(client, conn, channel_id); + if (channel) + return channel; + + if (query) { + idp = silc_id_payload_encode(channel_id, SILC_ID_CHANNEL); + silc_client_send_command(client, conn, SILC_COMMAND_IDENTIFY, + ++conn->cmd_ident, + 1, 5, idp->data, idp->len); + silc_buffer_free(idp); + } + + return NULL; +} + +/* Finds entry for server by the server name. */ + +SilcServerEntry silc_client_get_server(SilcClient client, + SilcClientConnection conn, + char *server_name) +{ + SilcIDCacheEntry id_cache; + SilcServerEntry entry; + + if (!silc_idcache_find_by_name_one(conn->server_cache, server_name, + &id_cache)) + return NULL; + + entry = (SilcServerEntry)id_cache->context; + + return entry; +} + +/* Finds entry for server by the server ID. */ + +SilcServerEntry silc_client_get_server_by_id(SilcClient client, + SilcClientConnection conn, + SilcServerID *server_id) +{ + SilcIDCacheEntry id_cache; + SilcServerEntry entry; + + if (!silc_idcache_find_by_id_one(conn->server_cache, (void *)server_id, + &id_cache)) + return NULL; + + entry = (SilcServerEntry)id_cache->context; + + return entry; +} + +/* Removes server from the cache by the server entry. */ + +bool silc_client_del_server(SilcClient client, SilcClientConnection conn, + SilcServerEntry server) +{ + bool ret = silc_idcache_del_by_context(conn->server_cache, server); + silc_free(server->server_name); + silc_free(server->server_info); + silc_free(server->server_id); + silc_free(server); + return ret; +} + +/* 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. */ + +void silc_client_nickname_format(SilcClient client, + SilcClientConnection conn, + SilcClientEntry client_entry) +{ + char *cp; + char *newnick = NULL; + int i, off = 0, len; + SilcClientEntry *clients; + uint32 clients_count; + + if (!client->params->nickname_format[0]) + return; + + if (!client_entry->nickname) + return; + + /* 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, + &clients_count); + if (!clients && !client->params->nickname_force_format) + return; + + cp = client->params->nickname_format; + while (*cp) { + if (*cp == '%') { + cp++; + continue; + } + + switch(*cp) { + case 'n': + /* Nickname */ + if (!client_entry->nickname) + break; + len = strlen(client_entry->nickname); + newnick = silc_realloc(newnick, sizeof(*newnick) * (off + len)); + memcpy(&newnick[off], client_entry->nickname, len); + off += len; + break; + case 'h': + /* Stripped hostname */ + if (!client_entry->hostname) + break; + len = strcspn(client_entry->hostname, "."); + newnick = silc_realloc(newnick, sizeof(*newnick) * (off + len)); + memcpy(&newnick[off], client_entry->hostname, len); + off += len; + break; + case 'H': + /* Full hostname */ + if (!client_entry->hostname) + break; + len = strlen(client_entry->hostname); + newnick = silc_realloc(newnick, sizeof(*newnick) * (off + len)); + memcpy(&newnick[off], client_entry->hostname, len); + off += len; + break; + case 's': + /* Stripped server name */ + if (!client_entry->server) + break; + len = strcspn(client_entry->server, "."); + newnick = silc_realloc(newnick, sizeof(*newnick) * (off + len)); + memcpy(&newnick[off], client_entry->server, len); + off += len; + break; + case 'S': + /* Full server name */ + if (!client_entry->server) + break; + len = strlen(client_entry->server); + newnick = silc_realloc(newnick, sizeof(*newnick) * (off + len)); + memcpy(&newnick[off], client_entry->server, len); + off += len; + break; + case 'a': + /* Ascending number */ + { + char tmp[6]; + int num, max = 1; + + if (clients_count == 1) + break; + + for (i = 0; i < clients_count; i++) { + if (strncmp(clients[i]->nickname, newnick, off)) + continue; + if (strlen(clients[i]->nickname) <= off) + continue; + num = atoi(&clients[i]->nickname[off]); + if (num > max) + max = num; + } + + memset(tmp, 0, sizeof(tmp)); + snprintf(tmp, sizeof(tmp) - 1, "%d", ++max); + len = strlen(tmp); + newnick = silc_realloc(newnick, sizeof(*newnick) * (off + len)); + memcpy(&newnick[off], tmp, len); + off += len; + } + break; + default: + /* Some other character in the string */ + newnick = silc_realloc(newnick, sizeof(*newnick) * (off + 1)); + memcpy(&newnick[off], cp, 1); + off++; + break; + } + + cp++; + } + + newnick = silc_realloc(newnick, sizeof(*newnick) * (off + 1)); + newnick[off] = 0; + + silc_free(client_entry->nickname); + client_entry->nickname = newnick; + silc_free(clients); +}