From 95f160373e5132ab9a735adb4ea07d60a3251aee Mon Sep 17 00:00:00 2001 From: Pekka Riikonen Date: Wed, 14 Mar 2001 12:28:18 +0000 Subject: [PATCH] Added WHOWAS command to server and client. --- CHANGES | 17 ++ README | 4 + apps/silc/client_ops.c | 48 ++++ apps/silcd/command.c | 310 +++++++++++++++++++++++++- doc/draft-riikonen-silc-spec-01.nroff | 7 +- lib/silcclient/command.c | 37 +++ lib/silcclient/command_reply.c | 69 +++++- 7 files changed, 482 insertions(+), 10 deletions(-) diff --git a/CHANGES b/CHANGES index b0cd2093..454faa11 100644 --- a/CHANGES +++ b/CHANGES @@ -1,3 +1,20 @@ +Wed Mar 14 13:18:16 EET 2001 Pekka Riikonen + + * Implemented WHOWAS command to the server. Added the functions: + + silc_server_command_whowas_parse, + silc_server_command_whowas_send_reply, + silc_server_command_whowas_from_client and + silc_server_command_whowas_from_server + + * Added argument to the WHOWAS command reply. Updated + the protocol specs accordingly. + + * Implemented WHOWAS command and command_reply to the client + library. + + Implemented the WHOWAS printing on the user interface. + Tue Mar 13 22:17:34 EET 2001 Pekka Riikonen * Added new argument to the WHOWAS command reply, the real name. diff --git a/README b/README index 0152f46d..b630091b 100644 --- a/README +++ b/README @@ -130,6 +130,10 @@ Following commands has been, at least partly, implemented: handling multiple same nicknames with this command is still missing. + /WHOWAS [@] [] + + Gives a little history information about a client. + /KICK [@] [] Kicks client from channel. You have to be at least channel diff --git a/apps/silc/client_ops.c b/apps/silc/client_ops.c index 2eff8bce..6b2f240e 100644 --- a/apps/silc/client_ops.c +++ b/apps/silc/client_ops.c @@ -429,6 +429,54 @@ void silc_command_reply(SilcClient client, SilcClientConnection conn, } break; + case SILC_COMMAND_WHOWAS: + { + char buf[1024], *nickname, *username, *realname; + int len; + + if (status == SILC_STATUS_ERR_NO_SUCH_NICK) { + char *tmp; + tmp = silc_argument_get_arg_type(silc_command_get_args(cmd_payload), + 3, NULL); + if (tmp) + client->ops->say(client, conn, "%s: %s", tmp, + silc_client_command_status_message(status)); + else + client->ops->say(client, conn, "%s", + silc_client_command_status_message(status)); + break; + } + + if (!success) + return; + + (void)va_arg(vp, SilcClientEntry); + nickname = va_arg(vp, char *); + username = va_arg(vp, char *); + realname = va_arg(vp, char *); + + memset(buf, 0, sizeof(buf)); + + if (nickname) { + len = strlen(nickname); + strncat(buf, nickname, len); + strncat(buf, " was ", 5); + } + + if (username) { + strncat(buf, username, strlen(nickname)); + } + + if (realname) { + strncat(buf, " (", 2); + strncat(buf, realname, strlen(realname)); + strncat(buf, ")", 1); + } + + client->ops->say(client, conn, "%s", buf); + } + break; + case SILC_COMMAND_JOIN: { unsigned int mode; diff --git a/apps/silcd/command.c b/apps/silcd/command.c index 985b8d49..b1c7260a 100644 --- a/apps/silcd/command.c +++ b/apps/silcd/command.c @@ -563,6 +563,15 @@ silc_server_command_whois_send_reply(SilcServerCommandContext cmd, for (i = 0; i < clients_count; i++) { entry = clients[i]; + if (entry->data.registered == FALSE) { + if (clients_count == 1) + silc_server_command_send_status_data(cmd, SILC_COMMAND_WHOIS, + SILC_STATUS_ERR_NO_SUCH_NICK, + 3, entry->nickname, + strlen(entry->nickname)); + continue; + } + if (count && i - 1 == count) break; @@ -581,7 +590,6 @@ silc_server_command_whois_send_reply(SilcServerCommandContext cmd, idp = silc_id_payload_encode(entry->id, SILC_ID_CLIENT); tmp = silc_argument_get_first_arg(cmd->args, NULL); - /* XXX */ { char nh[256], uh[256]; unsigned char idle[4]; @@ -903,25 +911,308 @@ SILC_SERVER_CMD_FUNC(whois) silc_server_command_free(cmd); } +/****************************************************************************** + + WHOWAS Functions + +******************************************************************************/ + +static int +silc_server_command_whowas_parse(SilcServerCommandContext cmd, + char **nickname, + char **server_name, + int *count) +{ + unsigned char *tmp; + unsigned int len; + + tmp = silc_argument_get_arg_type(cmd->args, 1, &len); + if (!tmp) { + silc_server_command_send_status_reply(cmd, SILC_COMMAND_WHOWAS, + SILC_STATUS_ERR_NOT_ENOUGH_PARAMS); + return FALSE; + } + + /* Get the nickname@server string and parse it. */ + if (strchr(tmp, '@')) { + len = strcspn(tmp, "@"); + *nickname = silc_calloc(len + 1, sizeof(char)); + memcpy(*nickname, tmp, len); + *server_name = silc_calloc(strlen(tmp) - len, sizeof(char)); + memcpy(*server_name, tmp + len + 1, strlen(tmp) - len - 1); + } else { + *nickname = strdup(tmp); + } + /* Get the max count of reply messages allowed */ + tmp = silc_argument_get_arg_type(cmd->args, 2, NULL); + if (tmp) + *count = atoi(tmp); + else + *count = 0; + + return TRUE; +} + +static void +silc_server_command_whowas_send_reply(SilcServerCommandContext cmd, + SilcClientEntry *clients, + unsigned int clients_count) +{ + SilcServer server = cmd->server; + char *tmp; + int i, count = 0, len; + SilcBuffer packet, idp; + SilcClientEntry entry = NULL; + SilcCommandStatus status; + unsigned short ident = silc_command_get_ident(cmd->payload); + char found = FALSE; + + status = SILC_STATUS_OK; + if (clients_count > 1) + status = SILC_STATUS_LIST_START; + + for (i = 0; i < clients_count; i++) { + entry = clients[i]; + + /* We will take only clients that are not valid anymore. They are the + ones that are not registered anymore but still have a ID. They + have disconnected us, and thus valid for WHOWAS. */ + if (entry->data.registered == TRUE) + continue; + if (entry->id == NULL) + continue; + + if (count && i - 1 == count) + break; + + found = TRUE; + + if (clients_count > 2) + status = SILC_STATUS_LIST_ITEM; + + if (clients_count > 1 && i == clients_count - 1) + status = SILC_STATUS_LIST_END; + + /* Sanity check, however these should never fail. However, as + this sanity check has been added here they have failed. */ + if (!entry->nickname || !entry->username) + continue; + + /* Send WHOWAS reply */ + idp = silc_id_payload_encode(entry->id, SILC_ID_CLIENT); + tmp = silc_argument_get_first_arg(cmd->args, NULL); + + { + char nh[256], uh[256]; + + memset(uh, 0, sizeof(uh)); + memset(nh, 0, sizeof(nh)); + + strncat(nh, entry->nickname, strlen(entry->nickname)); + if (!strchr(entry->nickname, '@')) { + strncat(nh, "@", 1); + len = entry->router ? strlen(entry->router->server_name) : + strlen(server->server_name); + strncat(nh, entry->router ? entry->router->server_name : + server->server_name, len); + } + + strncat(uh, entry->username, strlen(entry->username)); + if (!strchr(entry->username, '@')) { + strncat(uh, "@", 1); + strcat(uh, "*private*"); + } + + packet = + silc_command_reply_payload_encode_va(SILC_COMMAND_WHOWAS, + status, ident, 4, + 2, idp->data, idp->len, + 3, nh, strlen(nh), + 4, uh, strlen(uh), + 5, entry->userinfo, + strlen(entry->userinfo)); + } + + silc_server_packet_send(server, cmd->sock, SILC_PACKET_COMMAND_REPLY, + 0, packet->data, packet->len, FALSE); + + silc_buffer_free(packet); + silc_buffer_free(idp); + } + + if (found == FALSE && entry) + silc_server_command_send_status_data(cmd, SILC_COMMAND_WHOWAS, + SILC_STATUS_ERR_NO_SUCH_NICK, + 3, entry->nickname, + strlen(entry->nickname)); +} + +static int +silc_server_command_whowas_from_client(SilcServerCommandContext cmd) +{ + SilcServer server = cmd->server; + char *nick = NULL, *server_name = NULL; + int count = 0, clients_count = 0; + SilcClientEntry *clients = NULL; + int ret = 0; + + /* Protocol dictates that we must always send the received WHOWAS request + to our router if we are normal server, so let's do it now unless we + are standalone. We will not send any replies to the client until we + have received reply from the router. */ + if (server->server_type == SILC_SERVER && + !cmd->pending && !server->standalone) { + SilcBuffer tmpbuf; + unsigned short old_ident; + + old_ident = silc_command_get_ident(cmd->payload); + silc_command_set_ident(cmd->payload, silc_rng_get_rn16(server->rng)); + tmpbuf = silc_command_payload_encode_payload(cmd->payload); + + /* Send WHOWAS command to our router */ + silc_server_packet_send(server, (SilcSocketConnection) + server->router->connection, + SILC_PACKET_COMMAND, cmd->packet->flags, + tmpbuf->data, tmpbuf->len, TRUE); + + /* Reprocess this packet after received reply from router */ + silc_server_command_pending(server, SILC_COMMAND_WHOWAS, + silc_command_get_ident(cmd->payload), + silc_server_command_destructor, + silc_server_command_whois, + silc_server_command_dup(cmd)); + cmd->pending = TRUE; + + silc_command_set_ident(cmd->payload, old_ident); + + silc_buffer_free(tmpbuf); + ret = -1; + goto out; + } + + /* We are ready to process the command request. Let's search for the + requested client and send reply to the requesting client. */ + + /* Parse the whowas request */ + if (!silc_server_command_whowas_parse(cmd, &nick, &server_name, &count)) + return 0; + + /* Get all clients matching that nickname from local list */ + clients = silc_idlist_get_clients_by_nickname(server->local_list, + nick, server_name, + &clients_count); + if (!clients) + clients = silc_idlist_get_clients_by_hash(server->local_list, + nick, server->md5hash, + &clients_count); + + /* Check global list as well */ + if (!clients) { + clients = silc_idlist_get_clients_by_nickname(server->global_list, + nick, server_name, + &clients_count); + if (!clients) + clients = silc_idlist_get_clients_by_hash(server->global_list, + nick, server->md5hash, + &clients_count); + } + + if (!clients) { + /* Such client(s) really does not exist in the SILC network. */ + silc_server_command_send_status_data(cmd, SILC_COMMAND_WHOWAS, + SILC_STATUS_ERR_NO_SUCH_NICK, + 3, nick, strlen(nick)); + goto out; + } + + /* Send the command reply to the client */ + silc_server_command_whowas_send_reply(cmd, clients, clients_count); + + out: + if (clients) + silc_free(clients); + if (nick) + silc_free(nick); + if (server_name) + silc_free(server_name); + + return ret; +} + +static int +silc_server_command_whowas_from_server(SilcServerCommandContext cmd) +{ + SilcServer server = cmd->server; + char *nick = NULL, *server_name = NULL; + int count = 0, clients_count = 0; + SilcClientEntry *clients = NULL; + int ret = 0; + + /* Parse the whowas request */ + if (!silc_server_command_whowas_parse(cmd, &nick, &server_name, &count)) + return 0; + + /* Process the command request. Let's search for the requested client and + send reply to the requesting server. */ + + clients = silc_idlist_get_clients_by_nickname(server->local_list, + nick, server_name, + &clients_count); + if (!clients) + clients = silc_idlist_get_clients_by_hash(server->local_list, + nick, server->md5hash, + &clients_count); + + /* If we are router we will check our global list as well. */ + if (!clients && server->server_type == SILC_ROUTER) { + clients = silc_idlist_get_clients_by_nickname(server->global_list, + nick, server_name, + &clients_count); + if (!clients) + clients = silc_idlist_get_clients_by_hash(server->global_list, + nick, server->md5hash, + &clients_count); + } + + if (!clients) { + /* Such a client really does not exist in the SILC network. */ + silc_server_command_send_status_data(cmd, SILC_COMMAND_WHOWAS, + SILC_STATUS_ERR_NO_SUCH_NICK, + 3, nick, strlen(nick)); + goto out; + } + + /* Send the command reply to the client */ + silc_server_command_whowas_send_reply(cmd, clients, clients_count); + + out: + if (clients) + silc_free(clients); + if (nick) + silc_free(nick); + if (server_name) + silc_free(server_name); + + return ret; +} + /* Server side of command WHOWAS. */ SILC_SERVER_CMD_FUNC(whowas) { -#if 0 SilcServerCommandContext cmd = (SilcServerCommandContext)context; int ret = 0; SILC_SERVER_COMMAND_CHECK_ARGC(SILC_COMMAND_WHOWAS, cmd, 1, 2); if (cmd->sock->type == SILC_SOCKET_TYPE_CLIENT) - ret = silc_server_command_whois_from_client(cmd); + ret = silc_server_command_whowas_from_client(cmd); else if ((cmd->sock->type == SILC_SOCKET_TYPE_SERVER) || (cmd->sock->type == SILC_SOCKET_TYPE_ROUTER)) - ret = silc_server_command_whois_from_server(cmd); + ret = silc_server_command_whowas_from_server(cmd); if (!ret) silc_server_command_free(cmd); -#endif } /****************************************************************************** @@ -1005,6 +1296,15 @@ silc_server_command_identify_send_reply(SilcServerCommandContext cmd, for (i = 0; i < clients_count; i++) { entry = clients[i]; + if (entry->data.registered == FALSE) { + if (clients_count == 1) + silc_server_command_send_status_data(cmd, SILC_COMMAND_IDENTIFY, + SILC_STATUS_ERR_NO_SUCH_NICK, + 3, entry->nickname, + strlen(entry->nickname)); + continue; + } + if (count && i - 1 == count) break; diff --git a/doc/draft-riikonen-silc-spec-01.nroff b/doc/draft-riikonen-silc-spec-01.nroff index b587e55d..1cb7cb4e 100644 --- a/doc/draft-riikonen-silc-spec-01.nroff +++ b/doc/draft-riikonen-silc-spec-01.nroff @@ -1963,9 +1963,10 @@ List of all defined commands in SILC follows. Reply messages to the command: - Max Arguments: 4 - Arguments: (1) (2) [@] - (3) (4) [] + Max Arguments: 5 + Arguments: (1) (2) + (3) [@] (4) + (5) [] This command may reply with several command reply messages to form a list of results. In this case the status payload will include diff --git a/lib/silcclient/command.c b/lib/silcclient/command.c index 5f037a62..e01a9b19 100644 --- a/lib/silcclient/command.c +++ b/lib/silcclient/command.c @@ -268,8 +268,45 @@ SILC_CLIENT_CMD_FUNC(whois) silc_client_command_free(cmd); } +/* Command WHOWAS. This command is used to query history information about + specific user that used to exist in the network. */ + SILC_CLIENT_CMD_FUNC(whowas) { + SilcClientCommandContext cmd = (SilcClientCommandContext)context; + SilcClientConnection conn = cmd->conn; + SilcBuffer buffer; + + if (!cmd->conn) { + SILC_NOT_CONNECTED(cmd->client, cmd->conn); + COMMAND_ERROR; + goto out; + } + + if (cmd->argc < 2 || cmd->argc > 3) { + cmd->client->ops->say(cmd->client, conn, + "Usage: /WHOWAS [@] []"); + COMMAND_ERROR; + goto out; + } + + buffer = silc_command_payload_encode(SILC_COMMAND_WHOWAS, + cmd->argc - 1, ++cmd->argv, + ++cmd->argv_lens, ++cmd->argv_types, + 0); + silc_client_packet_send(cmd->client, cmd->conn->sock, + SILC_PACKET_COMMAND, NULL, 0, NULL, NULL, + buffer->data, buffer->len, TRUE); + silc_buffer_free(buffer); + cmd->argv--; + cmd->argv_lens--; + cmd->argv_types--; + + /* Notify application */ + COMMAND; + + out: + silc_client_command_free(cmd); } /* Command IDENTIFY. This command is used to query information about diff --git a/lib/silcclient/command_reply.c b/lib/silcclient/command_reply.c index c4861a8a..e21a3f52 100644 --- a/lib/silcclient/command_reply.c +++ b/lib/silcclient/command_reply.c @@ -69,8 +69,8 @@ SilcClientCommandReply silc_command_reply_list[] = const SilcCommandStatusMessage silc_command_status_messages[] = { - { STAT(NO_SUCH_NICK), "No such nickname" }, - { STAT(NO_SUCH_CHANNEL), "No such channel" }, + { STAT(NO_SUCH_NICK), "There was no such nickname" }, + { STAT(NO_SUCH_CHANNEL), "There was no such channel" }, { STAT(NO_SUCH_SERVER), "No such server" }, { STAT(TOO_MANY_TARGETS), "Duplicate recipients. No message delivered" }, { STAT(NO_RECIPIENT), "No recipient given" }, @@ -334,7 +334,72 @@ SILC_CLIENT_CMD_REPLY_FUNC(whois) SILC_CLIENT_CMD_REPLY_FUNC(whowas) { + SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context; + SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data; + SilcCommandStatus status; + SilcClientID *client_id; + SilcIDCacheEntry id_cache = NULL; + SilcClientEntry client_entry = NULL; + unsigned int len; + unsigned char *id_data, *tmp; + char *nickname, *username; + char *realname = NULL; + + SILC_LOG_DEBUG(("Start")); + + tmp = silc_argument_get_arg_type(cmd->args, 1, NULL); + SILC_GET16_MSB(status, tmp); + if (status != SILC_STATUS_OK && + status != SILC_STATUS_LIST_START && + status != SILC_STATUS_LIST_ITEM && + status != SILC_STATUS_LIST_END) { + COMMAND_REPLY_ERROR; + goto out; + } + + id_data = silc_argument_get_arg_type(cmd->args, 2, &len); + if (!id_data) { + COMMAND_REPLY_ERROR; + return; + } + + client_id = silc_id_payload_parse_id(id_data, len); + if (!client_id) { + COMMAND_REPLY_ERROR; + return; + } + + /* Get the client entry, if exists */ + if (silc_idcache_find_by_id_one(conn->client_cache, (void *)client_id, + SILC_ID_CLIENT, &id_cache)) + client_entry = (SilcClientEntry)id_cache->context; + silc_free(client_id); + + nickname = silc_argument_get_arg_type(cmd->args, 3, &len); + username = silc_argument_get_arg_type(cmd->args, 4, &len); + realname = silc_argument_get_arg_type(cmd->args, 5, &len); + if (!nickname || !username) { + COMMAND_REPLY_ERROR; + return; + } + /* Notify application. We don't save any history information to any + cache. Just pass the data to the application for displaying on + the screen. */ + COMMAND_REPLY((ARGS, client_entry, nickname, username, realname)); + + /* Pending callbacks are not executed if this was an list entry */ + if (status != SILC_STATUS_OK && + status != SILC_STATUS_LIST_END) { + silc_client_command_reply_free(cmd); + return; + } + + /* Execute any pending command callbacks */ + SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_WHOIS); + out: + SILC_CLIENT_PENDING_DESTRUCTOR(cmd, SILC_COMMAND_WHOIS); + silc_client_command_reply_free(cmd); } static void -- 2.24.0