X-Git-Url: http://git.silcnet.org/gitweb/?a=blobdiff_plain;f=lib%2Fsilcclient%2Fidlist.c;h=23674fc591fd42cd5ea803c7d6105c346021349b;hb=40f8443d8d3a6577336ee66d18e04d9ac4d956bb;hp=cd48b287516a3a6d55a0b25868fe0ec6438e008e;hpb=d53806603133b8b5714ce81ad691541a39963c2e;p=silc.git diff --git a/lib/silcclient/idlist.c b/lib/silcclient/idlist.c index cd48b287..23674fc5 100644 --- a/lib/silcclient/idlist.c +++ b/lib/silcclient/idlist.c @@ -1,10 +1,10 @@ /* - idlist.c + idlist.c Author: Pekka Riikonen - Copyright (C) 2001 - 2002 Pekka Riikonen + Copyright (C) 2001 - 2005 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 @@ -18,7 +18,7 @@ */ /* $Id$ */ -#include "silcincludes.h" +#include "silc.h" #include "silcclient.h" #include "client_internal.h" @@ -47,19 +47,29 @@ SilcClientEntry *silc_client_get_clients_local(SilcClient client, SilcIDCacheList list = NULL; SilcClientEntry entry, *clients; int i = 0; - bool found = FALSE; + SilcBool found = FALSE; + char *nicknamec; assert(client && conn); if (!nickname) return NULL; + /* Normalize nickname for search */ + nicknamec = silc_identifier_check(nickname, strlen(nickname), + SILC_STRING_UTF8, 128, NULL); + if (!nicknamec) + return NULL; + /* Find ID from cache */ - if (!silc_idcache_find_by_name(conn->internal->client_cache, - (char *)nickname, &list)) + if (!silc_idcache_find_by_name(conn->internal->client_cache, nicknamec, + &list)) { + silc_free(nicknamec); return NULL; + } if (!silc_idcache_list_count(list)) { silc_idcache_list_free(list); + silc_free(nicknamec); return NULL; } @@ -80,14 +90,14 @@ SilcClientEntry *silc_client_get_clients_local(SilcClient client, silc_idcache_list_first(list, &id_cache); while (id_cache) { entry = (SilcClientEntry)id_cache->context; - if (strcasecmp(entry->nickname, format)) { + if (!silc_utf8_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)) @@ -95,6 +105,8 @@ SilcClientEntry *silc_client_get_clients_local(SilcClient client, } } + silc_free(nicknamec); + if (list) silc_idcache_list_free(list); @@ -121,9 +133,12 @@ typedef struct { SilcGetClientCallback completion; void *context; char *nickname; - char *server; + SilcClientEntry *clients; + SilcUInt32 clients_count; } *GetClientInternal; +/* Completion for IDENTIFY */ + SILC_CLIENT_CMD_FUNC(get_client_callback) { GetClientInternal i = (GetClientInternal)context; @@ -132,7 +147,7 @@ SILC_CLIENT_CMD_FUNC(get_client_callback) /* Get the clients */ clients = silc_client_get_clients_local(i->client, i->conn, - i->nickname, i->server, + i->nickname, NULL, &clients_count); if (clients) { i->completion(i->client, i->conn, clients, clients_count, i->context); @@ -142,11 +157,127 @@ SILC_CLIENT_CMD_FUNC(get_client_callback) } silc_free(i->nickname); - silc_free(i->server); silc_free(i); } -/* Finds client entry or entries by the `nickname' and `server'. The +/* Completion for WHOIS */ + +SILC_CLIENT_CMD_FUNC(get_client_callback_wc) +{ + GetClientInternal i = (GetClientInternal)context; + SilcClientCommandReplyContext cmd = context2; + SilcClientID *client_id = NULL; + SilcClientEntry client_entry = NULL; + unsigned char *id_data; + SilcUInt32 len; + + /* Get the client entry just returned from server */ + id_data = silc_argument_get_arg_type(cmd->args, 2, &len); + if (id_data) + client_id = silc_id_payload_parse_id(id_data, len, NULL); + if (client_id) + client_entry = silc_client_get_client_by_id(i->client, + i->conn, client_id); + if (!client_entry) { + if (!SILC_STATUS_IS_ERROR(cmd->status) && + cmd->status != SILC_STATUS_OK && + cmd->status != SILC_STATUS_LIST_END) { + silc_free(client_id); + return; + } + + i->completion(i->client, i->conn, i->clients, i->clients_count, + i->context); + silc_free(client_id); + silc_free(i->clients); + silc_free(i->nickname); + silc_free(i); + return; + } + + /* Save the client */ + i->clients = silc_realloc(i->clients, + (sizeof(*i->clients) * (i->clients_count + 1))); + i->clients[i->clients_count] = client_entry; + i->clients_count++; + + /* Return if more data is expected */ + if (cmd->status != SILC_STATUS_OK && + cmd->status != SILC_STATUS_LIST_END) { + silc_free(client_id); + return; + } + + i->completion(i->client, i->conn, i->clients, i->clients_count, + i->context); + + silc_free(client_id); + silc_free(i->clients); + silc_free(i->nickname); + silc_free(i); +} + +/* Our own WHOIS reply processor. */ + +SILC_CLIENT_CMD_FUNC(get_client_callback_w) +{ + SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context; + SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data; + + SILC_LOG_DEBUG(("Start")); + + if (!silc_command_get_status(cmd->payload, NULL, NULL)) { + if (SILC_STATUS_IS_ERROR(cmd->status)) + goto out; + if (cmd->status == SILC_STATUS_LIST_END) + goto out; + goto err; + } + + /* Save WHOIS info */ + silc_client_command_reply_whois_save(cmd, cmd->status, FALSE); + + /* Call pending completion for each reply */ + if (cmd->status != SILC_STATUS_OK && + cmd->status != SILC_STATUS_LIST_END) { + if (cmd->callbacks[0].callback) + (*cmd->callbacks[0].callback)(cmd->callbacks[0].context, cmd); + silc_client_command_reply_free(cmd); + return; + } + + out: + SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_WHOIS); + + err: + /* If we received notify for invalid ID we'll remove the ID if we + have it cached. */ + if (cmd->error == SILC_STATUS_ERR_NO_SUCH_CLIENT_ID) { + SilcClientEntry client_entry; + SilcUInt32 tmp_len; + unsigned char *tmp = + silc_argument_get_arg_type(silc_command_get_args(cmd->payload), + 2, &tmp_len); + if (tmp) { + SilcClientID *client_id = silc_id_payload_parse_id(tmp, tmp_len, NULL); + if (client_id) { + client_entry = silc_client_get_client_by_id(cmd->client, conn, + client_id); + if (client_entry) + silc_client_del_client(cmd->client, conn, client_entry); + silc_free(client_id); + } + } + } + + /* Unregister this command reply */ + silc_client_command_unregister(cmd->client, SILC_COMMAND_WHOIS, + NULL, silc_client_command_reply_whois_i, + cmd->ident); + silc_client_command_reply_free(cmd); +} + +/* 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 @@ -155,27 +286,28 @@ SILC_CLIENT_CMD_FUNC(get_client_callback) 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) +void silc_client_get_clients_i(SilcClient client, + SilcClientConnection conn, + SilcCommand command, + const char *nickname, + const char *server, + SilcBuffer attributes, + SilcGetClientCallback completion, + void *context) { GetClientInternal i; int len; - char *userhost; + char *userhost = NULL; assert(client && conn); - if (!nickname) + if (!nickname && !attributes) return; i = silc_calloc(1, sizeof(*i)); i->client = client; i->conn = conn; - i->nickname = strdup(nickname); - i->server = server ? strdup(server) : NULL; + i->nickname = nickname ? strdup(nickname) : NULL; i->completion = completion; i->context = context; @@ -185,26 +317,65 @@ void silc_client_get_clients(SilcClient client, silc_strncat(userhost, len, nickname, strlen(nickname)); silc_strncat(userhost, len, "@", 1); silc_strncat(userhost, len, server, strlen(server)); - } else { + } else if (nickname) { userhost = silc_memdup(nickname, strlen(nickname)); } /* Register our own command reply for this command */ - silc_client_command_register(client, SILC_COMMAND_IDENTIFY, NULL, NULL, - silc_client_command_reply_identify_i, 0, - ++conn->cmd_ident); - - /* Send the command */ - silc_client_command_send(client, conn, SILC_COMMAND_IDENTIFY, - conn->cmd_ident, 1, 1, userhost, - strlen(userhost)); + if (command == SILC_COMMAND_IDENTIFY) { + silc_client_command_register(client, command, NULL, NULL, + silc_client_command_reply_identify_i, 0, + ++conn->cmd_ident); + /* Send the command */ + silc_client_command_send(client, conn, SILC_COMMAND_IDENTIFY, + conn->cmd_ident, 1, 1, userhost, + strlen(userhost)); + + /* Add pending callback */ + silc_client_command_pending(conn, command, conn->cmd_ident, + silc_client_command_get_client_callback, + (void *)i); + } else { + silc_client_command_register(client, command, NULL, NULL, + silc_client_command_get_client_callback_w, 0, + ++conn->cmd_ident); + /* Send the command */ + silc_client_command_send(client, conn, command, conn->cmd_ident, 2, + 1, userhost, userhost ? strlen(userhost) : 0, + 3, attributes ? attributes->data : NULL, + attributes ? attributes->len : 0); + + /* Add pending callback */ + silc_client_command_pending(conn, command, conn->cmd_ident, + silc_client_command_get_client_callback_wc, + (void *)i); + } + silc_free(userhost); +} - /* Add pending callback */ - silc_client_command_pending(conn, SILC_COMMAND_IDENTIFY, conn->cmd_ident, - silc_client_command_get_client_callback, - (void *)i); +void silc_client_get_clients(SilcClient client, + SilcClientConnection conn, + const char *nickname, + const char *server, + SilcGetClientCallback completion, + void *context) +{ + silc_client_get_clients_i(client, conn, SILC_COMMAND_IDENTIFY, + nickname, server, NULL, + completion, context); +} - silc_free(userhost); +void silc_client_get_clients_whois(SilcClient client, + SilcClientConnection conn, + const char *nickname, + const char *server, + SilcBuffer attributes, + SilcGetClientCallback completion, + void *context) +{ + silc_client_get_clients_i(client, conn, SILC_COMMAND_WHOIS, + nickname, server, attributes, + completion, context); } /* The old style function to find client entry. This is used by the @@ -217,37 +388,47 @@ SilcClientEntry silc_idlist_get_client(SilcClient client, SilcClientConnection conn, const char *nickname, const char *format, - bool query) + SilcBool query) { SilcIDCacheEntry id_cache; SilcIDCacheList list = NULL; SilcClientEntry entry = NULL; + char *nicknamec; SILC_LOG_DEBUG(("Start")); + /* Normalize nickname for search */ + nicknamec = silc_identifier_check(nickname, strlen(nickname), + SILC_STRING_UTF8, 128, NULL); + if (!nicknamec) + return NULL; + /* Find ID from cache */ if (!silc_idcache_find_by_name(conn->internal->client_cache, - (char *)nickname, &list)) { + nicknamec, &list)) { identify: if (query) { SILC_LOG_DEBUG(("Requesting Client ID from server")); - + /* Register our own command reply for this command */ silc_client_command_register(client, SILC_COMMAND_IDENTIFY, NULL, NULL, silc_client_command_reply_identify_i, 0, ++conn->cmd_ident); /* Send the command */ - silc_client_command_send(client, conn, SILC_COMMAND_IDENTIFY, + silc_client_command_send(client, conn, SILC_COMMAND_IDENTIFY, conn->cmd_ident, 1, 1, nickname, strlen(nickname)); if (list) silc_idcache_list_free(list); + silc_free(nicknamec); return NULL; } + + silc_free(nicknamec); return NULL; } @@ -263,7 +444,7 @@ SilcClientEntry silc_idlist_get_client(SilcClient client, while (id_cache) { entry = (SilcClientEntry)id_cache->context; - if (strcasecmp(entry->nickname, format)) { + if (!silc_utf8_strcasecmp(entry->nickname, format)) { if (!silc_idcache_list_next(list, &id_cache)) { entry = NULL; break; @@ -281,13 +462,14 @@ SilcClientEntry silc_idlist_get_client(SilcClient client, goto identify; } + silc_free(nicknamec); + if (list) silc_idcache_list_free(list); return entry; } - typedef struct { SilcClient client; SilcClientConnection conn; @@ -305,7 +487,7 @@ SILC_CLIENT_CMD_FUNC(get_clients_list_callback) SilcBuffer client_id_list = i->client_id_list; SilcClientEntry *clients = NULL; SilcUInt32 clients_count = 0; - bool found = FALSE; + SilcBool found = FALSE; int c; SILC_LOG_DEBUG(("Start")); @@ -318,6 +500,8 @@ SILC_CLIENT_CMD_FUNC(get_clients_list_callback) SILC_LOG_DEBUG(("Resolved all clients")); + clients = silc_calloc(i->list_count, sizeof(*clients)); + for (c = 0; c < i->list_count; c++) { SilcUInt16 idp_len; SilcClientID *client_id; @@ -332,13 +516,11 @@ SILC_CLIENT_CMD_FUNC(get_clients_list_callback) } /* Get the client entry */ - if (silc_idcache_find_by_id_one_ext(i->conn->internal->client_cache, - (void *)client_id, - NULL, NULL, + if (silc_idcache_find_by_id_one_ext(i->conn->internal->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++; found = TRUE; @@ -379,7 +561,7 @@ void silc_client_get_clients_by_list(SilcClient client, unsigned char **res_argv = NULL; SilcUInt32 *res_argv_lens = NULL, *res_argv_types = NULL, res_argc = 0; GetClientsByListInternal in; - bool wait_res = FALSE; + SilcBool wait_res = FALSE; assert(client && conn && client_id_list); @@ -397,7 +579,7 @@ void silc_client_get_clients_by_list(SilcClient client, SilcUInt16 idp_len; SilcClientID *client_id; SilcClientEntry entry; - bool ret; + SilcBool ret; /* Get Client ID */ SILC_GET16_MSB(idp_len, client_id_list->data + 2); @@ -411,7 +593,7 @@ void silc_client_get_clients_by_list(SilcClient client, /* Check if we have this client cached already. */ ret = silc_idcache_find_by_id_one_ext(conn->internal->client_cache, - (void *)client_id, NULL, NULL, + (void *)client_id, NULL, NULL, silc_hash_client_id_compare, NULL, &id_cache); @@ -424,9 +606,9 @@ void silc_client_get_clients_by_list(SilcClient client, if (entry->status & SILC_CLIENT_STATUS_RESOLVING) { /* Attach to this resolving and wait until it finishes */ silc_client_command_pending( - conn, SILC_COMMAND_NONE, + conn, SILC_COMMAND_NONE, entry->resolve_cmd_ident, - silc_client_command_get_clients_list_callback, + silc_client_command_get_clients_list_callback, (void *)in); wait_res = TRUE; in->res_count++; @@ -458,7 +640,7 @@ void silc_client_get_clients_by_list(SilcClient client, silc_buffer_pull(client_id_list, idp_len); } - silc_buffer_push(client_id_list, client_id_list->data - + silc_buffer_push(client_id_list, client_id_list->data - client_id_list->head); /* Query the client information from server if the list included clients @@ -470,7 +652,7 @@ void silc_client_get_clients_by_list(SilcClient client, 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, + silc_client_packet_send(client, conn->sock, SILC_PACKET_COMMAND, NULL, 0, NULL, NULL, res_cmd->data, res_cmd->len, TRUE); @@ -481,7 +663,7 @@ void silc_client_get_clients_by_list(SilcClient client, /* Process the applications request after reply has been received */ silc_client_command_pending(conn, SILC_COMMAND_IDENTIFY, conn->cmd_ident, - silc_client_command_get_clients_list_callback, + silc_client_command_get_clients_list_callback, (void *)in); in->res_count++; @@ -499,6 +681,175 @@ void silc_client_get_clients_by_list(SilcClient client, silc_client_command_get_clients_list_callback((void *)in, NULL); } +typedef struct { + SilcClient client; + SilcClientConnection conn; + SilcChannelID channel_id; + SilcGetClientCallback completion; + void *context; + int res_count; +} *GetClientsByChannelInternal; + +SILC_CLIENT_CMD_FUNC(get_clients_by_channel_cb) +{ + GetClientsByChannelInternal i = context; + SilcClientEntry *clients = NULL; + SilcUInt32 clients_count = 0; + SilcBool found = FALSE; + SilcChannelEntry channel; + SilcHashTableList htl; + SilcChannelUser chu; + + if (i->res_count) { + i->res_count--; + if (i->res_count) + return; + } + + channel = silc_client_get_channel_by_id(i->client, i->conn, &i->channel_id); + if (channel && !silc_hash_table_count(channel->user_list)) { + clients = silc_calloc(silc_hash_table_count(channel->user_list), + sizeof(*clients)); + silc_hash_table_list(channel->user_list, &htl); + while (silc_hash_table_get(&htl, NULL, (void *)&chu)) + clients[clients_count++] = chu->client; + silc_hash_table_list_reset(&htl); + found = TRUE; + } + + if (found) { + i->completion(i->client, i->conn, clients, clients_count, i->context); + silc_free(clients); + } else { + i->completion(i->client, i->conn, NULL, 0, i->context); + } + + silc_free(i); +} + +/* Gets client entries by the channel entry indicated by `channel'. Thus, + it resolves the clients currently on that channel. */ + +void silc_client_get_clients_by_channel(SilcClient client, + SilcClientConnection conn, + SilcChannelEntry channel, + SilcGetClientCallback completion, + void *context) +{ + GetClientsByChannelInternal in; + SilcHashTableList htl; + SilcChannelUser chu; + SilcClientEntry entry; + unsigned char **res_argv = NULL; + SilcUInt32 *res_argv_lens = NULL, *res_argv_types = NULL, res_argc = 0; + SilcBuffer idp; + SilcBool wait_res = FALSE; + + assert(client && conn && channel); + + SILC_LOG_DEBUG(("Start")); + + in = silc_calloc(1, sizeof(*in)); + in->client = client; + in->conn = conn; + in->channel_id = *channel->id; + in->completion = completion; + in->context = context; + + /* If user list does not exist, send USERS command. */ + if (!channel->user_list || !silc_hash_table_count(channel->user_list)) { + SILC_LOG_DEBUG(("Sending USERS")); + silc_client_command_register(client, SILC_COMMAND_USERS, NULL, NULL, + silc_client_command_reply_users_i, 0, + ++conn->cmd_ident); + silc_client_command_send(client, conn, SILC_COMMAND_USERS, + conn->cmd_ident, 1, 2, channel->channel_name, + strlen(channel->channel_name)); + silc_client_command_pending(conn, SILC_COMMAND_USERS, conn->cmd_ident, + silc_client_command_get_clients_by_channel_cb, + in); + return; + } + + silc_hash_table_list(channel->user_list, &htl); + while (silc_hash_table_get(&htl, NULL, (void *)&chu)) { + entry = chu->client; + + /* If the entry has incomplete info, then resolve it from the server. */ + if (!entry->nickname || !entry->realname) { + if (entry->status & SILC_CLIENT_STATUS_RESOLVING) { + /* Attach to this resolving and wait until it finishes */ + silc_client_command_pending( + conn, SILC_COMMAND_NONE, + entry->resolve_cmd_ident, + silc_client_command_get_clients_by_channel_cb, + (void *)in); + wait_res = TRUE; + in->res_count++; + continue; + } + entry->status |= SILC_CLIENT_STATUS_RESOLVING; + entry->resolve_cmd_ident = conn->cmd_ident + 1; + + idp = silc_id_payload_encode(entry->id, SILC_ID_CLIENT); + + /* No we don't have it, query it from the server. Assemble argument + table that will be sent for the WHOIS 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] = silc_memdup(idp->data, idp->len); + res_argv_lens[res_argc] = idp->len; + res_argv_types[res_argc] = res_argc + 4; + res_argc++; + + silc_buffer_free(idp); + } + } + silc_hash_table_list_reset(&htl); + + /* 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 WHOIS command to server */ + res_cmd = silc_command_payload_encode(SILC_COMMAND_WHOIS, + 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); + + /* Register our own command reply for this command */ + silc_client_command_register(client, SILC_COMMAND_WHOIS, NULL, NULL, + silc_client_command_reply_whois_i, 0, + conn->cmd_ident); + + /* Process the applications request after reply has been received */ + silc_client_command_pending( + conn, SILC_COMMAND_WHOIS, conn->cmd_ident, + silc_client_command_get_clients_by_channel_cb, + (void *)in); + in->res_count++; + + silc_buffer_free(res_cmd); + silc_free(res_argv); + silc_free(res_argv_lens); + silc_free(res_argv_types); + return; + } + + if (wait_res) + return; + + /* We have the clients in cache, get them and call the completion */ + silc_client_command_get_clients_by_channel_cb((void *)in, NULL); +} + /* Finds entry for client by the client's ID. Returns the entry or NULL if the entry was not found. */ @@ -512,12 +863,12 @@ SilcClientEntry silc_client_get_client_by_id(SilcClient client, if (!client_id) return NULL; - SILC_LOG_DEBUG(("Finding client by ID (%s)", + SILC_LOG_DEBUG(("Finding client by ID (%s)", silc_id_render(client_id, SILC_ID_CLIENT))); /* Find ID from cache */ if (!silc_idcache_find_by_id_one_ext(conn->internal->client_cache, - (void *)client_id, NULL, NULL, + (void *)client_id, NULL, NULL, silc_hash_client_id_compare, NULL, &id_cache)) return NULL; @@ -577,7 +928,7 @@ void silc_client_get_client_by_id_resolve(SilcClient client, i->client_id = silc_id_dup(client_id, SILC_ID_CLIENT); i->completion = completion; i->context = context; - + /* Register our own command reply for this command */ silc_client_command_register(client, SILC_COMMAND_WHOIS, NULL, NULL, silc_client_command_reply_whois_i, 0, @@ -593,7 +944,7 @@ void silc_client_get_client_by_id_resolve(SilcClient client, /* Add pending callback */ silc_client_command_pending(conn, SILC_COMMAND_WHOIS, conn->cmd_ident, - silc_client_command_get_client_by_id_callback, + silc_client_command_get_client_by_id_callback, (void *)i); } @@ -610,7 +961,7 @@ void silc_client_get_client_by_id_resolve(SilcClient client, SilcClientEntry silc_client_add_client(SilcClient client, SilcClientConnection conn, - char *nickname, char *username, + char *nickname, char *username, char *userinfo, SilcClientID *id, SilcUInt32 mode) { SilcClientEntry client_entry; @@ -623,7 +974,7 @@ silc_client_add_client(SilcClient client, SilcClientConnection conn, client_entry->id = id; client_entry->valid = TRUE; silc_parse_userfqdn(nickname, &nick, &client_entry->server); - silc_parse_userfqdn(username, &client_entry->username, + silc_parse_userfqdn(username, &client_entry->username, &client_entry->hostname); if (userinfo) client_entry->realname = strdup(userinfo); @@ -633,12 +984,30 @@ silc_client_add_client(SilcClient client, SilcClientConnection conn, client_entry->channels = silc_hash_table_alloc(1, silc_hash_ptr, NULL, NULL, NULL, NULL, NULL, TRUE); + /* Normalize nickname */ + if (client_entry->nickname) { + silc_free(nick); + nick = silc_identifier_check(client_entry->nickname, + strlen(client_entry->nickname), + SILC_STRING_UTF8, 128, NULL); + if (!nick) { + silc_free(client_entry->nickname); + silc_free(client_entry->username); + silc_free(client_entry->hostname); + silc_free(client_entry->server); + silc_hash_table_free(client_entry->channels); + silc_free(client_entry); + return NULL; + } + } + /* 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->internal->client_cache, nick, client_entry->id, + if (!silc_idcache_add(conn->internal->client_cache, nick, client_entry->id, (void *)client_entry, 0, NULL)) { + silc_free(nick); silc_free(client_entry->nickname); silc_free(client_entry->username); silc_free(client_entry->hostname); @@ -678,6 +1047,16 @@ void silc_client_update_client(SilcClient client, if (!client_entry->nickname && nickname) { silc_parse_userfqdn(nickname, &nick, &client_entry->server); client_entry->nickname = strdup(nick); + + /* Normalize nickname */ + silc_free(nick); + nick = silc_identifier_check(client_entry->nickname, + strlen(client_entry->nickname), + SILC_STRING_UTF8, 128, NULL); + if (!nick) + return; + + /* Format nickname */ silc_client_nickname_format(client, conn, client_entry); } client_entry->mode = mode; @@ -685,14 +1064,14 @@ void silc_client_update_client(SilcClient client, if (nick) { /* Remove the old cache entry and create a new one */ silc_idcache_del_by_context(conn->internal->client_cache, client_entry); - silc_idcache_add(conn->internal->client_cache, nick, client_entry->id, + silc_idcache_add(conn->internal->client_cache, nick, client_entry->id, client_entry, 0, NULL); } } /* Deletes the client entry and frees all memory. */ -void silc_client_del_client_entry(SilcClient client, +void silc_client_del_client_entry(SilcClient client, SilcClientConnection conn, SilcClientEntry client_entry) { @@ -705,6 +1084,8 @@ void silc_client_del_client_entry(SilcClient client, silc_free(client_entry->server); silc_free(client_entry->id); silc_free(client_entry->fingerprint); + if (client_entry->public_key) + silc_pkcs_public_key_free(client_entry->public_key); silc_hash_table_free(client_entry->channels); if (client_entry->send_key) silc_cipher_free(client_entry->send_key); @@ -719,17 +1100,19 @@ void silc_client_del_client_entry(SilcClient client, /* Removes client from the cache by the client entry. */ -bool silc_client_del_client(SilcClient client, SilcClientConnection conn, +SilcBool silc_client_del_client(SilcClient client, SilcClientConnection conn, SilcClientEntry client_entry) { - bool ret = silc_idcache_del_by_context(conn->internal->client_cache, + SilcBool ret = silc_idcache_del_by_context(conn->internal->client_cache, client_entry); - /* Remove from channels */ - silc_client_remove_from_channels(client, conn, client_entry); + if (ret) { + /* Remove from channels */ + silc_client_remove_from_channels(client, conn, client_entry); - /* Free the client entry data */ - silc_client_del_client_entry(client, conn, client_entry); + /* Free the client entry data */ + silc_client_del_client_entry(client, conn, client_entry); + } return ret; } @@ -739,10 +1122,11 @@ bool silc_client_del_client(SilcClient client, SilcClientConnection conn, SilcChannelEntry silc_client_add_channel(SilcClient client, SilcClientConnection conn, const char *channel_name, - SilcUInt32 mode, + SilcUInt32 mode, SilcChannelID *channel_id) { SilcChannelEntry channel; + char *channel_namec; SILC_LOG_DEBUG(("Start")); @@ -753,9 +1137,20 @@ SilcChannelEntry silc_client_add_channel(SilcClient client, channel->user_list = silc_hash_table_alloc(1, silc_hash_ptr, NULL, NULL, NULL, NULL, NULL, TRUE); + /* Normalize channel name */ + channel_namec = silc_channel_name_check(channel_name, strlen(channel_name), + SILC_STRING_UTF8, 256, NULL); + if (!channel_namec) { + silc_free(channel->channel_name); + silc_hash_table_free(channel->user_list); + silc_free(channel); + return NULL; + } + /* Put it to the ID cache */ - if (!silc_idcache_add(conn->internal->channel_cache, channel->channel_name, + if (!silc_idcache_add(conn->internal->channel_cache, channel_namec, (void *)channel->id, (void *)channel, 0, NULL)) { + silc_free(channel_namec); silc_free(channel->channel_name); silc_hash_table_free(channel->user_list); silc_free(channel); @@ -783,34 +1178,46 @@ static void silc_client_del_channel_foreach(void *key, void *context, /* Removes channel from the cache by the channel entry. */ -bool silc_client_del_channel(SilcClient client, SilcClientConnection conn, +SilcBool silc_client_del_channel(SilcClient client, SilcClientConnection conn, SilcChannelEntry channel) { - bool ret = silc_idcache_del_by_context(conn->internal->channel_cache, + SilcBool ret = silc_idcache_del_by_context(conn->internal->channel_cache, channel); SILC_LOG_DEBUG(("Start")); /* 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 + 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_hash_table_free(channel->user_list); silc_free(channel->channel_name); + silc_free(channel->topic); silc_free(channel->id); + 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_key) - silc_cipher_free(channel->old_channel_key); - if (channel->old_hmac) - silc_hmac_free(channel->old_hmac); - if (channel->rekey_task) - silc_schedule_task_del(conn->client->schedule, channel->rekey_task); + 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) + silc_cipher_free(key); + silc_dlist_uninit(channel->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) + silc_hmac_free(hmac); + silc_dlist_uninit(channel->old_hmacs); + } + silc_schedule_task_del_by_context(conn->client->schedule, channel); silc_client_del_channel_private_keys(client, conn, channel); silc_free(channel); return ret; @@ -819,24 +1226,33 @@ bool silc_client_del_channel(SilcClient client, SilcClientConnection conn, /* Replaces the channel ID of the `channel' to `new_id'. Returns FALSE if the ID could not be changed. */ -bool silc_client_replace_channel_id(SilcClient client, +SilcBool silc_client_replace_channel_id(SilcClient client, SilcClientConnection conn, SilcChannelEntry channel, SilcChannelID *new_id) { + char *channel_namec; + if (!new_id) return FALSE; - SILC_LOG_DEBUG(("Old Channel ID id(%s)", + SILC_LOG_DEBUG(("Old Channel ID id(%s)", silc_id_render(channel->id, SILC_ID_CHANNEL))); - SILC_LOG_DEBUG(("New Channel ID id(%s)", + SILC_LOG_DEBUG(("New Channel ID id(%s)", silc_id_render(new_id, SILC_ID_CHANNEL))); silc_idcache_del_by_id(conn->internal->channel_cache, channel->id); silc_free(channel->id); channel->id = new_id; - return silc_idcache_add(conn->internal->channel_cache, - channel->channel_name, + + /* Normalize channel name */ + channel_namec = silc_channel_name_check(channel->channel_name, + strlen(channel->channel_name), + SILC_STRING_UTF8, 256, NULL); + if (!channel_namec) + return FALSE; + + return silc_idcache_add(conn->internal->channel_cache, channel_namec, (void *)channel->id, (void *)channel, 0, NULL); } @@ -858,14 +1274,24 @@ SilcChannelEntry silc_client_get_channel(SilcClient client, SILC_LOG_DEBUG(("Start")); - if (!silc_idcache_find_by_name_one(conn->internal->channel_cache, channel, - &id_cache)) + /* Normalize name for search */ + channel = silc_channel_name_check(channel, strlen(channel), SILC_STRING_UTF8, + 256, NULL); + if (!channel) + return NULL; + + if (!silc_idcache_find_by_name_one(conn->internal->channel_cache, channel, + &id_cache)) { + silc_free(channel); return NULL; + } entry = (SilcChannelEntry)id_cache->context; SILC_LOG_DEBUG(("Found")); + silc_free(channel); + return entry; } @@ -886,7 +1312,7 @@ SilcChannelEntry silc_client_get_channel_by_id(SilcClient client, SILC_LOG_DEBUG(("Start")); - if (!silc_idcache_find_by_id_one(conn->internal->channel_cache, channel_id, + if (!silc_idcache_find_by_id_one(conn->internal->channel_cache, channel_id, &id_cache)) return NULL; @@ -900,27 +1326,85 @@ SilcChannelEntry silc_client_get_channel_by_id(SilcClient client, typedef struct { SilcClient client; SilcClientConnection conn; - SilcChannelID *channel_id; + union { + SilcChannelID *channel_id; + char *channel_name; + } u; SilcGetChannelCallback completion; void *context; -} *GetChannelByIDInternal; +} *GetChannelInternal; + +SILC_CLIENT_CMD_FUNC(get_channel_resolve_callback) +{ + GetChannelInternal i = (GetChannelInternal)context; + SilcChannelEntry entry; + + SILC_LOG_DEBUG(("Start")); + + /* Get the channel */ + entry = silc_client_get_channel(i->client, i->conn, i->u.channel_name); + if (entry) { + i->completion(i->client, i->conn, &entry, 1, i->context); + } else { + i->completion(i->client, i->conn, NULL, 0, i->context); + } + + silc_free(i->u.channel_name); + silc_free(i); +} + +/* Resolves channel entry from the server by the channel name. */ + +void silc_client_get_channel_resolve(SilcClient client, + SilcClientConnection conn, + char *channel_name, + SilcGetChannelCallback completion, + void *context) +{ + GetChannelInternal i = silc_calloc(1, sizeof(*i)); + + assert(client && conn && channel_name); + + SILC_LOG_DEBUG(("Start")); + + i->client = client; + i->conn = conn; + i->u.channel_name = strdup(channel_name); + i->completion = completion; + i->context = context; + + /* Register our own command reply for this command */ + silc_client_command_register(client, SILC_COMMAND_IDENTIFY, NULL, NULL, + silc_client_command_reply_identify_i, 0, + ++conn->cmd_ident); + + /* Send the command */ + silc_client_command_send(client, conn, SILC_COMMAND_IDENTIFY, + conn->cmd_ident, + 1, 3, channel_name, strlen(channel_name)); + + /* Add pending callback */ + silc_client_command_pending(conn, SILC_COMMAND_IDENTIFY, conn->cmd_ident, + silc_client_command_get_channel_resolve_callback, + (void *)i); +} SILC_CLIENT_CMD_FUNC(get_channel_by_id_callback) { - GetChannelByIDInternal i = (GetChannelByIDInternal)context; + GetChannelInternal i = (GetChannelInternal)context; SilcChannelEntry entry; SILC_LOG_DEBUG(("Start")); /* Get the channel */ - entry = silc_client_get_channel_by_id(i->client, i->conn, i->channel_id); + entry = silc_client_get_channel_by_id(i->client, i->conn, i->u.channel_id); if (entry) { i->completion(i->client, i->conn, &entry, 1, i->context); } else { i->completion(i->client, i->conn, NULL, 0, i->context); } - silc_free(i->channel_id); + silc_free(i->u.channel_id); silc_free(i); } @@ -933,7 +1417,7 @@ void silc_client_get_channel_by_id_resolve(SilcClient client, void *context) { SilcBuffer idp; - GetChannelByIDInternal i = silc_calloc(1, sizeof(*i)); + GetChannelInternal i = silc_calloc(1, sizeof(*i)); assert(client && conn && channel_id); @@ -941,10 +1425,10 @@ void silc_client_get_channel_by_id_resolve(SilcClient client, i->client = client; i->conn = conn; - i->channel_id = silc_id_dup(channel_id, SILC_ID_CHANNEL); + i->u.channel_id = silc_id_dup(channel_id, SILC_ID_CHANNEL); i->completion = completion; i->context = context; - + /* Register our own command reply for this command */ silc_client_command_register(client, SILC_COMMAND_IDENTIFY, NULL, NULL, silc_client_command_reply_identify_i, 0, @@ -952,14 +1436,14 @@ void silc_client_get_channel_by_id_resolve(SilcClient client, /* Send the command */ idp = silc_id_payload_encode(channel_id, SILC_ID_CHANNEL); - silc_client_command_send(client, conn, SILC_COMMAND_IDENTIFY, + silc_client_command_send(client, conn, SILC_COMMAND_IDENTIFY, conn->cmd_ident, 1, 5, idp->data, idp->len); silc_buffer_free(idp); /* Add pending callback */ silc_client_command_pending(conn, SILC_COMMAND_IDENTIFY, conn->cmd_ident, - silc_client_command_get_channel_by_id_callback, + silc_client_command_get_channel_by_id_callback, (void *)i); } @@ -978,12 +1462,22 @@ SilcServerEntry silc_client_get_server(SilcClient client, SILC_LOG_DEBUG(("Start")); + /* Normalize server name for search */ + server_name = silc_identifier_check(server_name, strlen(server_name), + SILC_STRING_UTF8, 256, NULL); + if (!server_name) + return NULL; + if (!silc_idcache_find_by_name_one(conn->internal->server_cache, - server_name, &id_cache)) + server_name, &id_cache)) { + silc_free(server_name); return NULL; + } entry = (SilcServerEntry)id_cache->context; + silc_free(server_name); + return entry; } @@ -1020,6 +1514,7 @@ SilcServerEntry silc_client_add_server(SilcClient client, SilcServerID *server_id) { SilcServerEntry server_entry; + char *server_namec = NULL; SILC_LOG_DEBUG(("Start")); @@ -1033,10 +1528,23 @@ SilcServerEntry silc_client_add_server(SilcClient client, if (server_info) server_entry->server_info = strdup(server_info); + /* Normalize server name */ + if (server_name) { + server_namec = silc_identifier_check(server_name, strlen(server_name), + SILC_STRING_UTF8, 256, NULL); + if (!server_namec) { + silc_free(server_entry->server_id); + silc_free(server_entry->server_name); + silc_free(server_entry->server_info); + silc_free(server_entry); + return NULL; + } + } + /* Add server to cache */ - if (!silc_idcache_add(conn->internal->server_cache, - server_entry->server_name, + if (!silc_idcache_add(conn->internal->server_cache, server_namec, server_entry->server_id, server_entry, 0, NULL)) { + silc_free(server_namec); silc_free(server_entry->server_id); silc_free(server_entry->server_name); silc_free(server_entry->server_info); @@ -1049,10 +1557,10 @@ SilcServerEntry silc_client_add_server(SilcClient client, /* Removes server from the cache by the server entry. */ -bool silc_client_del_server(SilcClient client, SilcClientConnection conn, +SilcBool silc_client_del_server(SilcClient client, SilcClientConnection conn, SilcServerEntry server) { - bool ret = silc_idcache_del_by_context(conn->internal->server_cache, server); + SilcBool ret = silc_idcache_del_by_context(conn->internal->server_cache, server); silc_free(server->server_name); silc_free(server->server_info); silc_free(server->server_id); @@ -1068,21 +1576,34 @@ void silc_client_update_server(SilcClient client, const char *server_name, const char *server_info) { + char *server_namec = NULL; + SILC_LOG_DEBUG(("Start")); - if (server_name && (!server_entry->server_name || - strcmp(server_entry->server_name, server_name))) { + 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); silc_free(server_entry->server_name); server_entry->server_name = strdup(server_name); - silc_idcache_add(conn->internal->server_cache, server_entry->server_name, - server_entry->server_id, - server_entry, 0, NULL); + + /* 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->server_id, + server_entry, 0, NULL); + } } - if (server_info && (!server_entry->server_info || - strcmp(server_entry->server_info, server_info))) { + if (server_info && + (!server_entry->server_info || + !silc_utf8_strcasecmp(server_entry->server_info, server_info))) { silc_free(server_entry->server_info); server_entry->server_info = strdup(server_info); } @@ -1093,14 +1614,14 @@ void silc_client_update_server(SilcClient client, 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, +void silc_client_nickname_format(SilcClient client, SilcClientConnection conn, SilcClientEntry client_entry) { char *cp; char *newnick = NULL; int i, off = 0, len; - bool freebase; + SilcBool freebase; SilcClientEntry *clients; SilcUInt32 clients_count = 0; SilcClientEntry unformatted = NULL; @@ -1128,12 +1649,26 @@ void silc_client_nickname_format(SilcClient client, if (clients[i]->valid && clients[i] != client_entry) len++; if (clients[i]->valid && clients[i] != client_entry && - !strcmp(clients[i]->nickname, client_entry->nickname)) + silc_utf8_strcasecmp(clients[i]->nickname, client_entry->nickname)) freebase = FALSE; } if (!len || freebase) return; + if (clients_count == 1) + unformatted = clients[0]; + else + for (i = 0; i < clients_count; i++) + if (silc_utf8_strncasecmp(clients[i]->nickname, client_entry->nickname, + strlen(clients[i]->nickname))) + unformatted = clients[i]; + + /* 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) + client_entry = unformatted; + cp = client->internal->params->nickname_format; while (*cp) { if (*cp == '%') { @@ -1196,16 +1731,11 @@ void silc_client_nickname_format(SilcClient client, char tmp[6]; int num, max = 1; - if (clients_count == 1) { - unformatted = clients[0]; + if (clients_count == 1) break; - } for (i = 0; i < clients_count; i++) { - if (!strncasecmp(clients[i]->nickname, client_entry->nickname, - strlen(clients[i]->nickname))) - unformatted = clients[i]; - if (strncasecmp(clients[i]->nickname, newnick, off)) + if (!silc_utf8_strncasecmp(clients[i]->nickname, newnick, off)) continue; if (strlen(clients[i]->nickname) <= off) continue; @@ -1236,12 +1766,6 @@ void silc_client_nickname_format(SilcClient client, newnick = silc_realloc(newnick, sizeof(*newnick) * (off + 1)); newnick[off] = 0; - /* 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) - client_entry = unformatted; - silc_free(client_entry->nickname); client_entry->nickname = newnick; silc_free(clients);