/* command_reply.c Author: Pekka Riikonen Copyright (C) 1997 - 2000 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 the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. */ /* * Command reply functions are "the otherside" of the command functions. * Reply to a command sent by server is handled by these functions. */ /* * $Id$ * $Log$ * Revision 1.9 2000/07/19 09:19:05 priikone * Enhancements to AWAY command. * * Revision 1.8 2000/07/19 07:06:51 priikone * Added AWAY command. * * Revision 1.7 2000/07/12 05:56:32 priikone * Major rewrite of ID Cache system. Support added for the new * ID cache system. * * Revision 1.6 2000/07/10 05:38:32 priikone * Added INFO command. * * Revision 1.5 2000/07/06 07:14:36 priikone * Fixes to NAMES command handling. * Fixes when leaving from channel. * * Revision 1.4 2000/07/05 06:12:34 priikone * Tweaked NAMES command reply for better. Should now show users * joined to a channel. * * Revision 1.3 2000/07/04 08:27:14 priikone * Changes to LEAVE command -- more consistent now and does error * handling better. Added INVITE, PING and part of NAMES commands. * SilcPacketContext is included into command structure. * * Revision 1.2 2000/07/03 05:49:49 priikone * Implemented LEAVE command. Minor bug fixes. * * Revision 1.1.1.1 2000/06/27 11:36:56 priikone * Imported from internal CVS/Added Log headers. * * */ #include "clientincludes.h" /* Client command reply list. */ SilcClientCommandReply silc_command_reply_list[] = { SILC_CLIENT_CMD_REPLY(whois, WHOIS), SILC_CLIENT_CMD_REPLY(whowas, WHOWAS), SILC_CLIENT_CMD_REPLY(identify, IDENTIFY), SILC_CLIENT_CMD_REPLY(nick, NICK), SILC_CLIENT_CMD_REPLY(list, LIST), SILC_CLIENT_CMD_REPLY(topic, TOPIC), SILC_CLIENT_CMD_REPLY(invite, INVITE), SILC_CLIENT_CMD_REPLY(quit, QUIT), SILC_CLIENT_CMD_REPLY(kill, KILL), SILC_CLIENT_CMD_REPLY(info, INFO), SILC_CLIENT_CMD_REPLY(away, AWAY), SILC_CLIENT_CMD_REPLY(connect, CONNECT), SILC_CLIENT_CMD_REPLY(ping, PING), SILC_CLIENT_CMD_REPLY(oper, OPER), SILC_CLIENT_CMD_REPLY(join, JOIN), SILC_CLIENT_CMD_REPLY(motd, MOTD), SILC_CLIENT_CMD_REPLY(umode, UMODE), SILC_CLIENT_CMD_REPLY(cmode, CMODE), SILC_CLIENT_CMD_REPLY(kick, KICK), SILC_CLIENT_CMD_REPLY(restart, RESTART), SILC_CLIENT_CMD_REPLY(close, CLOSE), SILC_CLIENT_CMD_REPLY(die, DIE), SILC_CLIENT_CMD_REPLY(silcoper, SILCOPER), SILC_CLIENT_CMD_REPLY(leave, LEAVE), SILC_CLIENT_CMD_REPLY(names, NAMES), { NULL, 0 }, }; /* Status message structure. Messages are defined below. */ typedef struct { SilcCommandStatus status; char *message; } SilcCommandStatusMessage; /* Status messages returned by the server */ #define STAT(x) SILC_STATUS_ERR_##x const SilcCommandStatusMessage silc_command_status_messages[] = { { STAT(NO_SUCH_NICK), "No such nickname" }, { STAT(NO_SUCH_CHANNEL), "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" }, { STAT(UNKNOWN_COMMAND), "Unknown command" }, { STAT(WILDCARDS), "Unknown command" }, { STAT(NO_CLIENT_ID), "No Client ID given" }, { STAT(NO_CHANNEL_ID), "No Channel ID given" }, { STAT(NO_SERVER_ID), "No Server ID given" }, { STAT(BAD_CLIENT_ID), "Bad Client ID" }, { STAT(BAD_CHANNEL_ID), "Bad Channel ID" }, { STAT(NO_SUCH_CLIENT_ID), "No such Client ID" }, { STAT(NO_SUCH_CHANNEL_ID),"No such Channel ID" }, { STAT(NICKNAME_IN_USE), "Nickname already exists" }, { STAT(NOT_ON_CHANNEL), "You are not on that channel" }, { STAT(USER_ON_CHANNEL), "User already on channel" }, { STAT(NOT_REGISTERED), "You have not registered" }, { STAT(NOT_ENOUGH_PARAMS), "Not enough parameters" }, { STAT(TOO_MANY_PARAMS), "Too many parameters" }, { STAT(PERM_DENIED), "Your host is not among the privileged" }, { STAT(BANNED_FROM_SERVER),"You are banned from this server" }, { STAT(BAD_PASSWORD), "Cannot join channel. Incorrect password" }, { STAT(CHANNEL_IS_FULL), "Cannot join channel. Channel is full" }, { STAT(NOT_INVITED), "Cannot join channel. You have not been invited" }, { STAT(BANNED_FROM_CHANNEL), "Cannot join channel. You have been banned" }, { STAT(UNKNOWN_MODE), "Unknown mode" }, { STAT(NOT_YOU), "Cannot change mode for other users" }, { STAT(NO_CHANNEL_PRIV), "Permission denied. You are not channel operator" }, { STAT(NO_SERVER_PRIV), "Permission denied. You are not server operator" }, { STAT(NO_ROUTER_PRIV), "Permission denied. You are not SILC operator" }, { STAT(BAD_NICKNAME), "Bad nickname" }, { STAT(BAD_CHANNEL), "Bad channel name" }, { STAT(AUTH_FAILED), "Authentication failed" }, { 0, NULL } }; /* Process received command reply. */ void silc_client_command_reply_process(SilcClient client, SilcSocketConnection sock, SilcPacketContext *packet) { SilcBuffer buffer = packet->buffer; SilcClientCommandReplyContext ctx; SilcCommandPayload payload; /* Get command reply payload from packet */ payload = silc_command_parse_payload(buffer); if (!payload) { /* Silently ignore bad reply packet */ SILC_LOG_DEBUG(("Bad command reply packet")); return; } /* Allocate command reply context. This must be free'd by the command reply routine receiving it. */ ctx = silc_calloc(1, sizeof(*ctx)); ctx->client = client; ctx->sock = sock; ctx->payload = payload; ctx->packet = packet; /* Check for pending commands and mark to be exeucted */ SILC_CLIENT_COMMAND_CHECK_PENDING(ctx); /* Execute command reply */ SILC_CLIENT_COMMAND_REPLY_EXEC(ctx); } /* Returns status message string */ static char * silc_client_command_status_message(SilcCommandStatus status) { int i; for (i = 0; silc_command_status_messages[i].message; i++) { if (silc_command_status_messages[i].status == status) break; } if (silc_command_status_messages[i].message == NULL) return NULL; return silc_command_status_messages[i].message; } /* Free command reply context and its internals. */ void silc_client_command_reply_free(SilcClientCommandReplyContext cmd) { if (cmd) { silc_command_free_payload(cmd->payload); silc_free(cmd); } } /* Received reply for WHOIS command. This maybe called several times for one WHOIS command as server may reply with list of results. */ SILC_CLIENT_CMD_REPLY_FUNC(whois) { SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context; SilcCommandStatus status; unsigned char *tmp; SILC_LOG_DEBUG(("Start")); tmp = silc_command_get_arg_type(cmd->payload, 1, NULL); SILC_GET16_MSB(status, tmp); if (status != SILC_STATUS_OK) { if (status == SILC_STATUS_ERR_NO_SUCH_NICK) { /* Take nickname which may be provided */ tmp = silc_command_get_arg_type(cmd->payload, 3, NULL); if (tmp) silc_say(cmd->client, "%s: %s", tmp, silc_client_command_status_message(status)); else silc_say(cmd->client, "%s", silc_client_command_status_message(status)); goto out; } else { silc_say(cmd->client, "%s", silc_client_command_status_message(status)); goto out; } } /* Display one whois reply */ if (status == SILC_STATUS_OK) { char buf[256]; int argc, len; unsigned char *id_data; char *nickname = NULL, *username = NULL; char *realname = NULL; memset(buf, 0, sizeof(buf)); argc = silc_command_get_arg_num(cmd->payload); id_data = silc_command_get_arg_type(cmd->payload, 2, NULL); nickname = silc_command_get_arg_type(cmd->payload, 3, &len); if (nickname) { strncat(buf, nickname, len); strncat(buf, " is ", 4); } username = silc_command_get_arg_type(cmd->payload, 4, &len); if (username) { strncat(buf, username, len); } realname = silc_command_get_arg_type(cmd->payload, 5, &len); if (realname) { strncat(buf, " (", 2); strncat(buf, realname, len); strncat(buf, ")", 1); } #if 0 /* Save received Client ID to ID cache */ /* XXX Maybe should not be saved as /MSG will get confused */ id = silc_id_str2id(id_data, SILC_ID_CLIENT); client->current_win->client_id_cache_count[(int)nickname[0] - 32] = silc_idcache_add(&client->current_win-> client_id_cache[(int)nickname[0] - 32], client->current_win-> client_id_cache_count[(int)nickname[0] - 32], strdup(nickname), SILC_ID_CLIENT, id, NULL); #endif silc_say(cmd->client, "%s", buf); } if (status == SILC_STATUS_LIST_START) { } if (status == SILC_STATUS_LIST_END) { } SILC_CLIENT_COMMAND_EXEC_PENDING(cmd, SILC_COMMAND_WHOIS); out: silc_client_command_reply_free(cmd); } SILC_CLIENT_CMD_REPLY_FUNC(whowas) { } /* Received reply for IDENTIFY command. This maybe called several times for one IDENTIFY command as server may reply with list of results. This is totally silent and does not print anything on screen. */ SILC_CLIENT_CMD_REPLY_FUNC(identify) { SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context; SilcClientWindow win = (SilcClientWindow)cmd->sock->user_data; SilcClientEntry client_entry; SilcCommandStatus status; unsigned char *tmp; SILC_LOG_DEBUG(("Start")); tmp = silc_command_get_arg_type(cmd->payload, 1, NULL); SILC_GET16_MSB(status, tmp); if (status != SILC_STATUS_OK) { if (status == SILC_STATUS_ERR_NO_SUCH_NICK) { /* Take nickname which may be provided */ tmp = silc_command_get_arg_type(cmd->payload, 3, NULL); if (tmp) silc_say(cmd->client, "%s: %s", tmp, silc_client_command_status_message(status)); else silc_say(cmd->client, "%s", silc_client_command_status_message(status)); goto out; } else { silc_say(cmd->client, "%s", silc_client_command_status_message(status)); goto out; } } /* Display one whois reply */ if (status == SILC_STATUS_OK) { unsigned char *id_data; char *nickname; id_data = silc_command_get_arg_type(cmd->payload, 2, NULL); nickname = silc_command_get_arg_type(cmd->payload, 3, NULL); /* Allocate client entry */ client_entry = silc_calloc(1, sizeof(*client_entry)); client_entry->id = silc_id_str2id(id_data, SILC_ID_CLIENT); client_entry->nickname = strdup(nickname); /* Save received Client ID to ID cache */ silc_idcache_add(win->client_cache, client_entry->nickname, SILC_ID_CLIENT, client_entry->id, client_entry, TRUE); } if (status == SILC_STATUS_LIST_START) { } if (status == SILC_STATUS_LIST_END) { } SILC_CLIENT_COMMAND_EXEC_PENDING(cmd, SILC_COMMAND_IDENTIFY); out: silc_client_command_reply_free(cmd); } /* Received reply for command NICK. If everything went without errors we just received our new Client ID. */ SILC_CLIENT_CMD_REPLY_FUNC(nick) { SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context; SilcClientWindow win = (SilcClientWindow)cmd->sock->user_data; SilcCommandStatus status; unsigned char *tmp, *id_string; int argc; SILC_LOG_DEBUG(("Start")); tmp = silc_command_get_arg_type(cmd->payload, 1, NULL); SILC_GET16_MSB(status, tmp); if (status != SILC_STATUS_OK) { silc_say(cmd->client, "Cannot set nickname: %s", silc_client_command_status_message(status)); goto out; } argc = silc_command_get_arg_num(cmd->payload); if (argc < 2 || argc > 2) { silc_say(cmd->client, "Cannot set nickname: bad reply to command"); goto out; } /* Take received Client ID */ id_string = silc_command_get_arg_type(cmd->payload, 2, NULL); silc_client_receive_new_id(cmd->client, cmd->sock, id_string); /* Update nickname on screen */ cmd->client->screen->bottom_line->nickname = win->nickname; silc_screen_print_bottom_line(cmd->client->screen, 0); out: silc_client_command_reply_free(cmd); } SILC_CLIENT_CMD_REPLY_FUNC(list) { } SILC_CLIENT_CMD_REPLY_FUNC(topic) { } /* Received reply to invite command. */ SILC_CLIENT_CMD_REPLY_FUNC(invite) { SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context; SilcCommandStatus status; unsigned char *tmp; tmp = silc_command_get_arg_type(cmd->payload, 1, NULL); SILC_GET16_MSB(status, tmp); if (status != SILC_STATUS_OK) { silc_say(cmd->client, "%s", silc_client_command_status_message(status)); silc_client_command_reply_free(cmd); return; } silc_client_command_reply_free(cmd); } SILC_CLIENT_CMD_REPLY_FUNC(quit) { } SILC_CLIENT_CMD_REPLY_FUNC(kill) { } /* Received reply to INFO command. We receive the server ID and some information about the server user requested. */ SILC_CLIENT_CMD_REPLY_FUNC(info) { SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context; SilcClient client = cmd->client; SilcCommandStatus status; unsigned char *tmp; tmp = silc_command_get_arg_type(cmd->payload, 1, NULL); SILC_GET16_MSB(status, tmp); if (status != SILC_STATUS_OK) { silc_say(cmd->client, "%s", silc_client_command_status_message(status)); silc_client_command_reply_free(cmd); return; } /* Get server ID */ tmp = silc_command_get_arg_type(cmd->payload, 2, NULL); if (!tmp) goto out; /* XXX save server id */ /* Get server info */ tmp = silc_command_get_arg_type(cmd->payload, 3, NULL); if (!tmp) goto out; silc_say(client, "Info: %s", tmp); out: silc_client_command_reply_free(cmd); } SILC_CLIENT_CMD_REPLY_FUNC(away) { } SILC_CLIENT_CMD_REPLY_FUNC(connect) { } /* Received reply to PING command. The reply time is shown to user. */ SILC_CLIENT_CMD_REPLY_FUNC(ping) { SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context; SilcClientWindow win = (SilcClientWindow)cmd->sock->user_data; SilcCommandStatus status; void *id; char *tmp; int i; time_t diff, curtime; tmp = silc_command_get_arg_type(cmd->payload, 1, NULL); SILC_GET16_MSB(status, tmp); if (status != SILC_STATUS_OK) { silc_say(cmd->client, "%s", silc_client_command_status_message(status)); goto out; } curtime = time(NULL); id = silc_id_str2id(cmd->packet->src_id, cmd->packet->src_id_type); for (i = 0; i < win->ping_count; i++) { if (!SILC_ID_SERVER_COMPARE(win->ping[i].dest_id, id)) { diff = curtime - win->ping[i].start_time; silc_say(cmd->client, "Ping reply from %s: %d second%s", win->ping[i].dest_name, diff, diff == 1 ? "" : "s"); win->ping[i].start_time = 0; silc_free(win->ping[i].dest_id); win->ping[i].dest_id = NULL; silc_free(win->ping[i].dest_name); win->ping[i].dest_name = NULL; goto out; } } out: silc_client_command_reply_free(cmd); } SILC_CLIENT_CMD_REPLY_FUNC(oper) { } /* Received reply for JOIN command. */ SILC_CLIENT_CMD_REPLY_FUNC(join) { SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context; SilcClient client = cmd->client; SilcCommandStatus status; unsigned int argc, mode; unsigned char *id_string; char *topic, *tmp, *channel_name; SILC_LOG_DEBUG(("Start")); tmp = silc_command_get_arg_type(cmd->payload, 1, NULL); SILC_GET16_MSB(status, tmp); if (status != SILC_STATUS_OK) { silc_say(cmd->client, "%s", silc_client_command_status_message(status)); goto out; } argc = silc_command_get_arg_num(cmd->payload); if (argc < 3 || argc > 4) { silc_say(cmd->client, "Cannot join channel: Bad reply packet"); goto out; } /* Get channel name */ tmp = silc_command_get_arg_type(cmd->payload, 2, NULL); if (!tmp) { silc_say(cmd->client, "Cannot join channel: Bad reply packet"); goto out; } channel_name = strdup(tmp); /* Get Channel ID */ id_string = silc_command_get_arg_type(cmd->payload, 3, NULL); if (!id_string) { silc_say(cmd->client, "Cannot join channel: Bad reply packet"); goto out; } /* Get channel mode */ tmp = silc_command_get_arg_type(cmd->payload, 4, NULL); if (tmp) SILC_GET32_MSB(mode, tmp); else mode = 0; /* Get topic */ topic = silc_command_get_arg_type(cmd->payload, 5, NULL); /* Save received Channel ID */ silc_client_new_channel_id(cmd->client, cmd->sock, channel_name, mode, id_string); /* Print channel name on screen */ client->screen->bottom_line->channel = channel_name; silc_screen_print_bottom_line(client->screen, 0); if (topic) silc_say(client, "Topic for %s: %s", channel_name, topic); out: silc_client_command_reply_free(cmd); } SILC_CLIENT_CMD_REPLY_FUNC(motd) { } SILC_CLIENT_CMD_REPLY_FUNC(umode) { } SILC_CLIENT_CMD_REPLY_FUNC(cmode) { } SILC_CLIENT_CMD_REPLY_FUNC(kick) { } SILC_CLIENT_CMD_REPLY_FUNC(restart) { } SILC_CLIENT_CMD_REPLY_FUNC(close) { } SILC_CLIENT_CMD_REPLY_FUNC(die) { } SILC_CLIENT_CMD_REPLY_FUNC(silcoper) { } /* Reply to LEAVE command. */ SILC_CLIENT_CMD_REPLY_FUNC(leave) { SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context; SilcCommandStatus status; unsigned char *tmp; tmp = silc_command_get_arg_type(cmd->payload, 1, NULL); SILC_GET16_MSB(status, tmp); if (status != SILC_STATUS_OK) silc_say(cmd->client, "%s", silc_client_command_status_message(status)); silc_client_command_reply_free(cmd); } /* Reply to NAMES command. Received list of client names on the channel we requested. */ SILC_CLIENT_CMD_REPLY_FUNC(names) { SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context; SilcClientWindow win = (SilcClientWindow)cmd->sock->user_data; SilcCommandStatus status; SilcIDCacheEntry id_cache = NULL; SilcChannelEntry channel; SilcChannelID *channel_id = NULL; SilcBuffer client_id_list; unsigned char *tmp; char *name_list; int i, len1, len2, list_count = 0; SILC_LOG_DEBUG(("Start")); tmp = silc_command_get_arg_type(cmd->payload, 1, NULL); SILC_GET16_MSB(status, tmp); if (status != SILC_STATUS_OK) { silc_say(cmd->client, "%s", silc_client_command_status_message(status)); goto out; } /* Get channel ID */ tmp = silc_command_get_arg_type(cmd->payload, 2, NULL); if (!tmp) { silc_say(cmd->client, "Cannot get user list: Bad reply packet"); goto out; } channel_id = silc_id_str2id(tmp, SILC_ID_CHANNEL); /* Get the name list of the channel */ name_list = silc_command_get_arg_type(cmd->payload, 3, &len1); if (!name_list) { silc_say(cmd->client, "Cannot get user list: Bad reply packet"); goto out; } /* Get Client ID list */ tmp = silc_command_get_arg_type(cmd->payload, 4, &len2); if (!tmp) { silc_say(cmd->client, "Cannot get user list: Bad reply packet"); goto out; } client_id_list = silc_buffer_alloc(len2); silc_buffer_pull_tail(client_id_list, len2); silc_buffer_put(client_id_list, tmp, len2); /* Get the channel name */ if (!silc_idcache_find_by_id_one(win->channel_cache, (void *)channel_id, SILC_ID_CHANNEL, &id_cache)) goto out; channel = (SilcChannelEntry)id_cache->context; /* If there is pending command we know that user has called this command and we will handle the name list differently. */ if (cmd->callback) { /* We will resolve all the necessary information about the people on the channel. Only after that will we display the user list. */ for (i = 0; i < len1; i++) { /* XXX */ } silc_client_command_pending_del(SILC_COMMAND_NAMES); } else { /* there is no pending callback it means that this command reply has been received without calling the command, ie. server has sent the reply without getting the command from us first. This happens with SILC servers that sends NAMES reply after joining to a channel. */ /* Remove commas from list */ for (i = 0; i < len1; i++) if (name_list[i] == ',') { name_list[i] = ' '; list_count++; } silc_say(cmd->client, "Users on %s: %s", channel->channel_name, name_list); } /* Cache the received name list and client ID's. This cache expires whenever server sends notify message to channel. It means two things; some user has joined or leaved the channel. */ for (i = 0; i < list_count; i++) { int nick_len = strcspn(name_list, " "); char *nickname = silc_calloc(nick_len, sizeof(*nickname)); SilcClientID *client_id; SilcClientEntry client; memcpy(nickname, name_list, nick_len); client_id = silc_id_str2id(client_id_list->data, SILC_ID_CLIENT_LEN); silc_buffer_pull(client_id_list, SILC_ID_CLIENT_LEN); client = silc_calloc(1, sizeof(*client)); client->id = client_id; client->nickname = nickname; silc_idcache_add(win->client_cache, nickname, SILC_ID_CLIENT, client_id, (void *)client, TRUE); name_list = name_list + nick_len + 1; } silc_buffer_free(client_id_list); out: if (channel_id) silc_free(channel_id); silc_client_command_reply_free(cmd); } /* Private message received. This processes the private message and finally displays it on the screen. Note that private messages are not really commands except on user interface which is reason why this handling resides here. */ SILC_CLIENT_CMD_REPLY_FUNC(msg) { SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context; SilcClient client = cmd->client; SilcClientWindow win = (SilcClientWindow)cmd->sock->user_data; SilcBuffer buffer = (SilcBuffer)cmd->packet->buffer; unsigned short nick_len; unsigned char *nickname, *message; /* Get nickname */ silc_buffer_unformat(buffer, SILC_STR_UI16_NSTRING_ALLOC(&nickname, &nick_len), SILC_STR_END); silc_buffer_pull(buffer, 2 + nick_len); message = silc_calloc(buffer->len + 1, sizeof(char)); memcpy(message, buffer->data, buffer->len); silc_print(client, "*%s* %s", nickname, message); /* See if we are away (gone). If we are away we will reply to the sender with the set away message. */ if (win->away && win->away->away) { SilcClientID *remote_id; SilcClientEntry remote_client; SilcIDCacheEntry id_cache; if (cmd->packet->src_id_type != SILC_ID_CLIENT) goto out; remote_id = silc_id_str2id(cmd->packet->src_id, SILC_ID_CLIENT); if (!remote_id) goto out; if (!SILC_ID_CLIENT_COMPARE(remote_id, win->local_id)) goto out; /* Check whether we know this client already */ if (!silc_idcache_find_by_id_one(win->client_cache, remote_id, SILC_ID_CLIENT, &id_cache)) { /* Allocate client entry */ remote_client = silc_calloc(1, sizeof(*remote_client)); remote_client->id = remote_id; remote_client->nickname = strdup(nickname); /* Save the client to cache */ silc_idcache_add(win->client_cache, remote_client->nickname, SILC_ID_CLIENT, remote_client->id, remote_client, TRUE); } else { silc_free(remote_id); remote_client = (SilcClientEntry)id_cache->context; } /* Send the away message */ silc_client_packet_send_private_message(client, cmd->sock, remote_client, win->away->away, strlen(win->away->away), TRUE); } out: memset(message, 0, buffer->len); silc_free(message); silc_free(nickname); }