GNU General Public License for more details.
*/
-/*
- * $Id$
- * $Log$
- * Revision 1.13 2000/07/20 10:17:25 priikone
- * Added dynamic protocol registering/unregistering support. The
- * patch was provided by cras.
- *
- * Revision 1.12 2000/07/19 07:07:34 priikone
- * Save packet on private message's command context.
- *
- * Revision 1.11 2000/07/18 12:20:39 priikone
- * Added ^U functionality, clears input line (patch form cras).
- *
- * Revision 1.10 2000/07/18 06:53:15 priikone
- * Allow partial command strings in comparison.
- *
- * Revision 1.9 2000/07/14 06:13:19 priikone
- * Moved all the generic packet sending, encryption, reception,
- * decryption and processing functions to library as they were
- * duplicated code with the server. Now client uses the generic
- * routines which is a lot cleaner.
- *
- * Revision 1.8 2000/07/12 05:56:32 priikone
- * Major rewrite of ID Cache system. Support added for the new
- * ID cache system.
- *
- * Revision 1.7 2000/07/10 05:40:33 priikone
- * Minor bug fixes.
- *
- * Revision 1.6 2000/07/07 06:54:16 priikone
- * Print channel name when receiving channel message to non-current
- * channel.
- *
- * 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:05 priikone
- * Global cosmetic changes.
- *
- * Revision 1.3 2000/07/04 08:29:12 priikone
- * Added support for PING command. The ping times are calculated
- * and showed to the user.
- *
- * Revision 1.2 2000/07/03 05:49:48 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"
-
-/* Static function prototypes */
-static int silc_client_bad_keys(unsigned char key);
-static void silc_client_clear_input(SilcClient client);
-static void silc_client_process_message(SilcClient client);
-static char *silc_client_parse_command(unsigned char *buffer);
+/* $Id$ */
+
+#include "clientlibincludes.h"
/* Static task callback prototypes */
-SILC_TASK_CALLBACK(silc_client_update_clock);
-SILC_TASK_CALLBACK(silc_client_run_commands);
-SILC_TASK_CALLBACK(silc_client_process_key_press);
SILC_TASK_CALLBACK(silc_client_connect_to_server_start);
SILC_TASK_CALLBACK(silc_client_connect_to_server_second);
SILC_TASK_CALLBACK(silc_client_connect_to_server_final);
SILC_TASK_CALLBACK(silc_client_packet_process);
SILC_TASK_CALLBACK(silc_client_packet_parse_real);
-SilcClientWindow silc_client_create_main_window(SilcClient client);
-SilcClientWindow silc_client_add_window(SilcClient client,
- int is_current);
-void silc_client_packet_parse(SilcPacketParserContext *parser_context);
-void silc_client_packet_parse_type(SilcClient client,
- SilcSocketConnection sock,
- SilcPacketContext *packet);
-void silc_client_private_message_process(SilcClient client,
- SilcSocketConnection sock,
- SilcPacketContext *packet);
-
-/* Definitions from version.h */
-extern char *silc_version;
-extern char *silc_name;
-extern char *silc_fullname;
+static void silc_client_packet_parse(SilcPacketParserContext *parser_context);
+static void silc_client_packet_parse_type(SilcClient client,
+ SilcSocketConnection sock,
+ SilcPacketContext *packet);
/* Allocates new client object. This has to be done before client may
work. After calling this one must call silc_client_init to initialize
- the client. */
+ the client. The `application' is application specific user data pointer
+ and caller must free it. */
-int silc_client_alloc(SilcClient *new_client)
+SilcClient silc_client_alloc(SilcClientOperations *ops, void *application)
{
+ SilcClient new_client;
- *new_client = silc_calloc(1, sizeof(**new_client));
- (*new_client)->input_buffer = NULL;
- (*new_client)->screen = NULL;
- (*new_client)->windows = NULL;
- (*new_client)->windows_count = 0;
- (*new_client)->current_win = NULL;
+ new_client = silc_calloc(1, sizeof(*new_client));
+ new_client->application = application;
+ new_client->ops = ops;
- return TRUE;
+ return new_client;
}
/* Free's client object */
int silc_client_init(SilcClient client)
{
-
SILC_LOG_DEBUG(("Initializing client"));
- assert(client);
-
- client->username = silc_get_username();
- client->realname = silc_get_real_name();
-
- /* Register all configured ciphers, PKCS and hash functions. */
- client->config->client = (void *)client;
- silc_client_config_register_ciphers(client->config);
- silc_client_config_register_pkcs(client->config);
- silc_client_config_register_hashfuncs(client->config);
/* Initialize hash functions for client to use */
silc_hash_alloc("md5", &client->md5hash);
silc_rng_init(client->rng);
silc_math_primegen_init(); /* XXX */
- /* Load public and private key */
- if (silc_client_load_keys(client) == FALSE)
- goto err0;
-
- /* Register the task queues. In SILC we have by default three task queues.
- One task queue for non-timeout tasks which perform different kind of
- I/O on file descriptors, timeout task queue for timeout tasks, and,
- generic non-timeout task queue whose tasks apply to all connections. */
- silc_task_queue_alloc(&client->io_queue, TRUE);
- if (!client->io_queue) {
- goto err0;
- }
- silc_task_queue_alloc(&client->timeout_queue, TRUE);
- if (!client->timeout_queue) {
- goto err1;
- }
- silc_task_queue_alloc(&client->generic_queue, TRUE);
- if (!client->generic_queue) {
- goto err1;
- }
-
/* Register protocols */
silc_client_protocols_register();
/* Initialize the scheduler */
- silc_schedule_init(client->io_queue, client->timeout_queue,
- client->generic_queue, 5000);
-
- /* Register the main task that is used in client. This received
- the key pressings. */
- if (silc_task_register(client->io_queue, fileno(stdin),
- silc_client_process_key_press,
- (void *)client, 0, 0,
- SILC_TASK_FD,
- SILC_TASK_PRI_NORMAL) == NULL) {
- goto err2;
- }
-
- /* Register timeout task that updates clock every minute. */
- if (silc_task_register(client->timeout_queue, 0,
- silc_client_update_clock,
- (void *)client,
- silc_client_time_til_next_min(), 0,
- SILC_TASK_TIMEOUT,
- SILC_TASK_PRI_LOW) == NULL) {
- goto err2;
- }
-
- if (client->config->commands) {
- /* Run user configured commands with timeout */
- if (silc_task_register(client->timeout_queue, 0,
- silc_client_run_commands,
- (void *)client, 0, 1,
- SILC_TASK_TIMEOUT,
- SILC_TASK_PRI_LOW) == NULL) {
- goto err2;
- }
- }
-
- /* Allocate the input buffer used to save typed characters */
- client->input_buffer = silc_buffer_alloc(SILC_SCREEN_INPUT_WIN_SIZE);
- silc_buffer_pull_tail(client->input_buffer,
- SILC_BUFFER_END(client->input_buffer));
-
- /* Initialize the screen */
- client->screen = silc_screen_init();
- silc_client_create_main_window(client);
- client->screen->input_buffer = client->input_buffer->data;
- silc_screen_print_coordinates(client->screen, 0);
+ silc_schedule_init(&client->io_queue, &client->timeout_queue,
+ &client->generic_queue, 5000);
return TRUE;
-
- err0:
- silc_task_queue_free(client->timeout_queue);
- err1:
- silc_task_queue_free(client->io_queue);
- err2:
- return FALSE;
}
/* Stops the client. This is called to stop the client and thus to stop
silc_client_protocols_unregister();
- SILC_LOG_DEBUG(("Client client"));
+ SILC_LOG_DEBUG(("Client stopped"));
}
/* Runs the client. */
silc_schedule();
}
-/* Creates the main window used in SILC client. This is called always
- at the initialization of the client. If user wants to create more
- than one windows a new windows are always created by calling
- silc_client_add_window. */
+/* Allocates and adds new connection to the client. This adds the allocated
+ connection to the connection table and returns a pointer to it. A client
+ can have multiple connections to multiple servers. Every connection must
+ be added to the client using this function. User data `context' may
+ be sent as argument. */
-SilcClientWindow silc_client_create_main_window(SilcClient client)
+SilcClientConnection silc_client_add_connection(SilcClient client,
+ void *context)
{
- SilcClientWindow win;
- void *screen;
-
- SILC_LOG_DEBUG(("Creating main window"));
-
- assert(client->screen != NULL);
-
- client->screen->u_stat_line.program_name = silc_name;
- client->screen->u_stat_line.program_version = silc_version;
-
- /* Create windows */
- win = silc_calloc(1, sizeof(*win));
- win->nickname = silc_get_username();
- win->local_id = NULL;
- win->local_id_data = NULL;
- win->local_id_data_len = 0;
- win->remote_host = NULL;
- win->remote_port = -1;
- win->sock = NULL;
-
- /* Initialize ID caches */
- win->client_cache = silc_idcache_alloc(0);
- win->channel_cache = silc_idcache_alloc(0);
- win->server_cache = silc_idcache_alloc(0);
-
- /* Create the actual screen */
- screen = (void *)silc_screen_create_output_window(client->screen);
- silc_screen_create_input_window(client->screen);
- silc_screen_init_upper_status_line(client->screen);
- silc_screen_init_output_status_line(client->screen);
- win->screen = screen;
-
- client->screen->bottom_line->nickname = win->nickname;
- silc_screen_print_bottom_line(client->screen, 0);
-
- /* Add the window to windows table */
- client->windows = silc_calloc(1, sizeof(*client->windows));
- client->windows[client->windows_count] = win;
- client->windows_count = 1;
-
- /* Automatically becomes the current active window */
- client->current_win = win;
-
- return win;
-}
-
-/* Allocates and adds new window to the client. This allocates new
- physical window and internal window for connection specific data.
- All the connection specific data is always saved into a window
- since connection is always associated to a active window. */
-
-SilcClientWindow silc_client_add_window(SilcClient client,
- int is_current)
-{
- SilcClientWindow win;
-
- assert(client->screen != NULL);
-
- win = silc_calloc(1, sizeof(*win));
+ SilcClientConnection conn;
+ int i;
- /* Add the pointers */
- win->screen = silc_screen_add_output_window(client->screen);
- win->sock = NULL;
+ conn = silc_calloc(1, sizeof(*conn));
/* Initialize ID caches */
- win->client_cache = silc_idcache_alloc(0);
- win->channel_cache = silc_idcache_alloc(0);
- win->server_cache = silc_idcache_alloc(0);
-
- /* Add the window to windows table */
- client->windows = silc_realloc(client->windows, sizeof(*client->windows)
- * (client->windows_count + 1));
- client->windows[client->windows_count] = win;
- client->windows_count++;
-
- if (is_current == TRUE)
- client->current_win = win;
-
- return win;
-}
-
-/* The main task on SILC client. This processes the key pressings user
- has made. */
-
-SILC_TASK_CALLBACK(silc_client_process_key_press)
-{
- SilcClient client = (SilcClient)context;
- int c;
-
- /* There is data pending in stdin, this gets it directly */
- c = wgetch(client->screen->input_win);
- if (silc_client_bad_keys(c))
- return;
-
- SILC_LOG_DEBUG(("Pressed key: %d", c));
-
- switch(c) {
- /*
- * Special character handling
- */
- case KEY_UP:
- case KEY_DOWN:
- break;
- case KEY_RIGHT:
- /* Right arrow */
- SILC_LOG_DEBUG(("RIGHT"));
- silc_screen_input_cursor_right(client->screen);
- break;
- case KEY_LEFT:
- /* Left arrow */
- SILC_LOG_DEBUG(("LEFT"));
- silc_screen_input_cursor_left(client->screen);
- break;
- case KEY_BACKSPACE:
- case KEY_DC:
- case '\177':
- case '\b':
- /* Backspace */
- silc_screen_input_backspace(client->screen);
- break;
- case '\011':
- /* Tabulator */
- break;
- case KEY_IC:
- /* Insert switch. Turns on/off insert on input window */
- silc_screen_input_insert(client->screen);
- break;
- case CTRL('j'):
- case '\r':
- /* Enter, Return. User pressed enter we are ready to
- process the message. */
- silc_client_process_message(client);
- break;
- case CTRL('l'):
- /* Refresh screen, Ctrl^l */
- silc_screen_refresh_all(client->screen);
- break;
- case CTRL('a'):
- case KEY_HOME:
- case KEY_BEG:
- /* Beginning, Home */
- silc_screen_input_cursor_home(client->screen);
- break;
- case CTRL('e'):
- case KEY_END:
- /* End */
- silc_screen_input_cursor_end(client->screen);
- break;
- case KEY_LL:
- /* End */
- break;
- case CTRL('g'):
- /* Bell, Ctrl^g */
- beep();
- break;
- case KEY_DL:
- case CTRL('u'):
- /* Delete line */
- silc_client_clear_input(client);
- break;
- default:
- /*
- * Other characters
- */
- if (c < 32) {
- /* Control codes are printed as reversed */
- c = (c & 127) | 64;
- wattron(client->screen->input_win, A_REVERSE);
- silc_screen_input_print(client->screen, c);
- wattroff(client->screen->input_win, A_REVERSE);
- } else {
- /* Normal character */
- silc_screen_input_print(client->screen, c);
- }
- }
-
- silc_screen_print_coordinates(client->screen, 0);
- silc_screen_refresh_win(client->screen->input_win);
-}
-
-static int silc_client_bad_keys(unsigned char key)
-{
- /* these are explained in curses.h */
- switch(key) {
- case KEY_SF:
- case KEY_SR:
- case KEY_NPAGE:
- case KEY_PPAGE:
- case KEY_PRINT:
- case KEY_A1:
- case KEY_A3:
- case KEY_B2:
- case KEY_C1:
- case KEY_C3:
- case KEY_UNDO:
- case KEY_EXIT:
- case '\v': /* VT */
- case '\E': /* we ignore ESC */
- return TRUE;
- default:
- return FALSE;
- }
-}
-
-/* Clears input buffer */
-
-static void silc_client_clear_input(SilcClient client)
-{
- silc_buffer_clear(client->input_buffer);
- silc_buffer_pull_tail(client->input_buffer,
- SILC_BUFFER_END(client->input_buffer));
- silc_screen_input_reset(client->screen);
-}
-
-/* Processes messages user has typed on the screen. This either sends
- a packet out to network or if command were written executes it. */
-
-static void silc_client_process_message(SilcClient client)
-{
- unsigned char *data;
- unsigned int len;
-
- SILC_LOG_DEBUG(("Start"));
-
- data = client->input_buffer->data;
- len = strlen(data);
-
- if (data[0] == '/' && data[1] != ' ') {
- /* Command */
- unsigned int argc = 0;
- unsigned char **argv, *tmpcmd;
- unsigned int *argv_lens, *argv_types;
- SilcClientCommand *cmd;
- SilcClientCommandContext ctx;
-
- /* Get the command */
- tmpcmd = silc_client_parse_command(data);
-
- /* Find command match */
- for (cmd = silc_command_list; cmd->name; cmd++) {
- if (!strncmp(cmd->name, tmpcmd, strlen(tmpcmd)))
- break;
+ conn->client_cache = silc_idcache_alloc(0);
+ conn->channel_cache = silc_idcache_alloc(0);
+ conn->server_cache = silc_idcache_alloc(0);
+ conn->client = client;
+ conn->context = context;
+
+ /* Add the connection to connections table */
+ for (i = 0; i < client->conns_count; i++)
+ if (client->conns && !client->conns[i]) {
+ client->conns[i] = conn;
+ return conn;
}
- if (cmd->name == NULL) {
- silc_say(client, "Invalid command: %s", tmpcmd);
- silc_free(tmpcmd);
- goto out;
- }
-
- /* Now parse all arguments */
- silc_client_parse_command_line(data, &argv, &argv_lens,
- &argv_types, &argc, cmd->max_args);
- silc_free(tmpcmd);
-
- SILC_LOG_DEBUG(("Exeuting command: %s", cmd->name));
-
- /* Allocate command context. This and its internals must be free'd
- by the command routine receiving it. */
- ctx = silc_calloc(1, sizeof(*ctx));
- ctx->client = client;
- ctx->sock = client->current_win->sock;
- ctx->argc = argc;
- ctx->argv = argv;
- ctx->argv_lens = argv_lens;
- ctx->argv_types = argv_types;
-
- /* Execute command */
- (*cmd->cb)(ctx);
+ client->conns = silc_realloc(client->conns, sizeof(*client->conns)
+ * (client->conns_count + 1));
+ client->conns[client->conns_count] = conn;
+ client->conns_count++;
- } else {
- /* Normal message to a channel */
- if (len && client->current_win->current_channel &&
- client->current_win->current_channel->on_channel == TRUE) {
- silc_print(client, "> %s", data);
- silc_client_packet_send_to_channel(client,
- client->current_win->sock,
- client->current_win->current_channel,
- data, strlen(data), TRUE);
- }
- }
-
- out:
- /* Clear the input buffer */
- silc_client_clear_input(client);
+ return conn;
}
-/* Returns the command fetched from user typed command line */
+/* Removes connection from client. */
-static char *silc_client_parse_command(unsigned char *buffer)
+void silc_client_del_connection(SilcClient client, SilcClientConnection conn)
{
- char *ret;
- const char *cp = buffer;
- int len;
-
- len = strcspn(cp, " ");
- ret = silc_to_upper((char *)++cp);
- ret[len - 1] = 0;
-
- return ret;
-}
-
-/* Parses user typed command line. At most `max_args' is taken. Rest
- of the line will be allocated as the last argument if there are more
- than `max_args' arguments in the line. Note that the command name
- is counted as one argument and is saved. */
-
-void silc_client_parse_command_line(unsigned char *buffer,
- unsigned char ***parsed,
- unsigned int **parsed_lens,
- unsigned int **parsed_types,
- unsigned int *parsed_num,
- unsigned int max_args)
-{
- int i, len = 0;
- int argc = 0;
- const char *cp = buffer;
-
- /* Take the '/' away */
- cp++;
-
- *parsed = silc_calloc(1, sizeof(**parsed));
- *parsed_lens = silc_calloc(1, sizeof(**parsed_lens));
-
- /* Get the command first */
- len = strcspn(cp, " ");
- (*parsed)[0] = silc_to_upper((char *)cp);
- (*parsed_lens)[0] = len;
- cp += len + 1;
- argc++;
-
- /* Parse arguments */
- if (strchr(cp, ' ') || strlen(cp) != 0) {
- for (i = 1; i < max_args; i++) {
-
- if (i != max_args - 1)
- len = strcspn(cp, " ");
- else
- len = strlen(cp);
-
- *parsed = silc_realloc(*parsed, sizeof(**parsed) * (argc + 1));
- *parsed_lens = silc_realloc(*parsed_lens,
- sizeof(**parsed_lens) * (argc + 1));
- (*parsed)[argc] = silc_calloc(len + 1, sizeof(char));
- memcpy((*parsed)[argc], cp, len);
- (*parsed_lens)[argc] = len;
- argc++;
-
- cp += len;
- if (strlen(cp) == 0)
- break;
- else
- cp++;
- }
- }
-
- /* Save argument types. Protocol defines all argument types but
- this implementation makes sure that they are always in correct
- order hence this simple code. */
- *parsed_types = silc_calloc(argc, sizeof(**parsed_types));
- for (i = 0; i < argc; i++)
- (*parsed_types)[i] = i;
-
- *parsed_num = argc;
-}
-
-/* Updates clock on the screen every minute. */
-
-SILC_TASK_CALLBACK(silc_client_update_clock)
-{
- SilcClient client = (SilcClient)context;
-
- /* Update the clock on the screen */
- silc_screen_print_clock(client->screen);
-
- /* Re-register this same task */
- silc_task_register(qptr, 0, silc_client_update_clock, context,
- silc_client_time_til_next_min(), 0,
- SILC_TASK_TIMEOUT,
- SILC_TASK_PRI_LOW);
-
- silc_screen_refresh_win(client->screen->input_win);
-}
-
-/* Runs commands user configured in configuration file. This is
- called when initializing client. */
-
-SILC_TASK_CALLBACK(silc_client_run_commands)
-{
- SilcClient client = (SilcClient)context;
- SilcClientConfigSectionCommand *cs;
-
- SILC_LOG_DEBUG(("Start"));
-
- cs = client->config->commands;
- while(cs) {
- unsigned int argc = 0;
- unsigned char **argv, *tmpcmd;
- unsigned int *argv_lens, *argv_types;
- SilcClientCommand *cmd;
- SilcClientCommandContext ctx;
-
- /* Get the command */
- tmpcmd = silc_client_parse_command(cs->command);
+ int i;
- for (cmd = silc_command_list; cmd->name; cmd++) {
- if (!strcmp(cmd->name, tmpcmd))
- break;
- }
-
- if (cmd->name == NULL) {
- silc_say(client, "Invalid command: %s", tmpcmd);
- silc_free(tmpcmd);
- continue;
+ for (i = 0; i < client->conns_count; i++)
+ if (client->conns[i] == conn) {
+ silc_free(conn);
+ client->conns[i] = NULL;
}
-
- /* Now parse all arguments */
- silc_client_parse_command_line(cs->command, &argv, &argv_lens,
- &argv_types, &argc, cmd->max_args);
- silc_free(tmpcmd);
-
- SILC_LOG_DEBUG(("Exeuting command: %s", cmd->name));
-
- /* Allocate command context. This and its internals must be free'd
- by the command routine receiving it. */
- ctx = silc_calloc(1, sizeof(*ctx));
- ctx->client = client;
- ctx->sock = client->current_win->sock;
- ctx->argc = argc;
- ctx->argv = argv;
- ctx->argv_lens = argv_lens;
- ctx->argv_types = argv_types;
-
- /* Execute command */
- (*cmd->cb)(ctx);
-
- cs = cs->next;
- }
}
/* Internal context for connection process. This is needed as we
doing asynchronous connecting. */
typedef struct {
SilcClient client;
+ SilcClientConnection conn;
SilcTask task;
int sock;
char *host;
return sock;
}
-/* Connects to remote server */
+/* Connects to remote server. This is the main routine used to connect
+ to SILC server. Returns -1 on error and the created socket otherwise.
+ The `context' is user context that is saved into the SilcClientConnection
+ that is created after the connection is created. */
int silc_client_connect_to_server(SilcClient client, int port,
- char *host)
+ char *host, void *context)
{
SilcClientInternalConnectContext *ctx;
+ SilcClientConnection conn;
SILC_LOG_DEBUG(("Connecting to port %d of server %s",
port, host));
- silc_say(client, "Connecting to port %d of server %s", port, host);
+ conn = silc_client_add_connection(client, context);
+ conn->remote_host = strdup(host);
+ conn->remote_port = port;
- client->current_win->remote_host = strdup(host);
- client->current_win->remote_port = port;
+ client->ops->say(client, conn,
+ "Connecting to port %d of server %s", port, host);
/* Allocate internal context for connection process. This is
needed as we are doing async connecting. */
ctx = silc_calloc(1, sizeof(*ctx));
ctx->client = client;
+ ctx->conn = conn;
ctx->host = strdup(host);
ctx->port = port;
ctx->tries = 0;
SilcClientInternalConnectContext *ctx =
(SilcClientInternalConnectContext *)context;
SilcClient client = ctx->client;
+ SilcClientConnection conn = ctx->conn;
SilcProtocol protocol;
SilcClientKEInternalContext *proto_ctx;
int opt, opt_len = sizeof(opt);
if (opt != 0) {
if (ctx->tries < 2) {
/* Connection failed but lets try again */
- silc_say(ctx->client, "Could not connect to server %s: %s",
- ctx->host, strerror(opt));
- silc_say(client, "Connecting to port %d of server %s resumed",
- ctx->port, ctx->host);
+ client->ops->say(client, conn, "Could not connect to server %s: %s",
+ ctx->host, strerror(opt));
+ client->ops->say(client, conn,
+ "Connecting to port %d of server %s resumed",
+ ctx->port, ctx->host);
/* Unregister old connection try */
silc_schedule_unset_listen_fd(fd);
ctx->tries++;
} else {
/* Connection failed and we won't try anymore */
- silc_say(ctx->client, "Could not connect to server %s: %s",
- ctx->host, strerror(opt));
+ client->ops->say(client, conn, "Could not connect to server %s: %s",
+ ctx->host, strerror(opt));
silc_schedule_unset_listen_fd(fd);
silc_net_close_connection(fd);
silc_task_unregister(client->io_queue, ctx->task);
silc_free(ctx);
+
+ /* Notify application of failure */
+ client->ops->connect(client, conn, FALSE);
}
return;
}
silc_free(ctx);
/* Allocate new socket connection object */
- silc_socket_alloc(fd, SILC_SOCKET_TYPE_SERVER,
- (void *)client->current_win,
- &client->current_win->sock);
- if (client->current_win->sock == NULL) {
- silc_say(client, "Error: Could not allocate connection socket");
+ silc_socket_alloc(fd, SILC_SOCKET_TYPE_SERVER, (void *)conn, &conn->sock);
+ if (conn->sock == NULL) {
+ client->ops->say(client, conn,
+ "Error: Could not allocate connection socket");
silc_net_close_connection(fd);
+ client->ops->connect(client, conn, FALSE);
return;
}
- client->current_win->sock->hostname = client->current_win->remote_host;
- client->current_win->sock->port = client->current_win->remote_port;
+
+ conn->nickname = strdup(client->username);
+ conn->sock->hostname = conn->remote_host;
+ conn->sock->port = conn->remote_port;
/* Allocate internal Key Exchange context. This is sent to the
protocol as context. */
proto_ctx = silc_calloc(1, sizeof(*proto_ctx));
proto_ctx->client = (void *)client;
- proto_ctx->sock = client->current_win->sock;
+ proto_ctx->sock = conn->sock;
proto_ctx->rng = client->rng;
proto_ctx->responder = FALSE;
&protocol, (void *)proto_ctx,
silc_client_connect_to_server_second);
if (!protocol) {
- silc_say(client, "Error: Could not start authentication protocol");
+ client->ops->say(client, conn,
+ "Error: Could not start authentication protocol");
+ client->ops->connect(client, conn, FALSE);
return;
}
- client->current_win->sock->protocol = protocol;
+ conn->sock->protocol = protocol;
/* Register the connection for network input and output. This sets
that scheduler will listen for incoming packets for this connection
silc_free(ctx->dest_id);
ctx->sock->protocol = NULL;
silc_free(ctx);
+
+ /* Notify application of failure */
+ client->ops->connect(client, ctx->sock->user_data, FALSE);
return;
}
proto_ctx->dest_id = ctx->dest_id;
/* Resolve the authentication method to be used in this connection */
- proto_ctx->auth_meth = SILC_PROTOCOL_CONN_AUTH_NONE;
- if (client->config->conns) {
- SilcClientConfigSectionConnection *conn = NULL;
-
- /* Check if we find a match from user configured connections */
- conn = silc_client_config_find_connection(client->config,
- sock->hostname,
- sock->port);
- if (conn) {
- /* Match found. Use the configured authentication method */
- proto_ctx->auth_meth = conn->auth_meth;
- if (conn->auth_data) {
- proto_ctx->auth_data = strdup(conn->auth_data);
- proto_ctx->auth_data_len = strlen(conn->auth_data);
- }
- } else {
- /* No match found. Resolve by sending AUTH_REQUEST to server */
+ if (!client->ops->get_auth_method(client, sock->user_data, sock->hostname,
+ sock->port, &proto_ctx->auth_meth,
+ &proto_ctx->auth_data,
+ &proto_ctx->auth_data_len))
+ {
+ /* XXX do AUTH_REQUEST resolcing with server */
proto_ctx->auth_meth = SILC_PROTOCOL_CONN_AUTH_NONE;
}
- } else {
- /* XXX Resolve by sending AUTH_REQUEST to server */
- proto_ctx->auth_meth = SILC_PROTOCOL_CONN_AUTH_NONE;
- }
/* Free old protocol as it is finished now */
silc_protocol_free(protocol);
SilcClientConnAuthInternalContext *ctx =
(SilcClientConnAuthInternalContext *)protocol->context;
SilcClient client = (SilcClient)ctx->client;
- SilcClientWindow win = (SilcClientWindow)ctx->sock->user_data;
+ SilcClientConnection conn = (SilcClientConnection)ctx->sock->user_data;
SilcBuffer packet;
SILC_LOG_DEBUG(("Start"));
if (ctx->dest_id)
silc_free(ctx->dest_id);
silc_free(ctx);
- win->sock->protocol = NULL;
+ conn->sock->protocol = NULL;
+
+ /* Notify application of failure */
+ client->ops->connect(client, ctx->sock->user_data, FALSE);
return;
}
silc_buffer_free(packet);
/* Save remote ID. */
- win->remote_id = ctx->dest_id;
- win->remote_id_data = silc_id_id2str(ctx->dest_id, SILC_ID_CHANNEL);
- win->remote_id_data_len = SILC_ID_CHANNEL_LEN;
+ conn->remote_id = ctx->dest_id;
+ conn->remote_id_data = silc_id_id2str(ctx->dest_id, SILC_ID_SERVER);
+ conn->remote_id_data_len = SILC_ID_SERVER_LEN;
- silc_say(client, "Connected to port %d of host %s",
- win->remote_port, win->remote_host);
+ client->ops->say(client, conn, "Connected to port %d of host %s",
+ conn->remote_port, conn->remote_host);
- client->screen->bottom_line->connection = win->remote_host;
- silc_screen_print_bottom_line(client->screen, 0);
+ /* Notify application of successful connection */
+ client->ops->connect(client, conn, TRUE);
silc_protocol_free(protocol);
if (ctx->auth_data)
if (ctx->dest_id)
silc_free(ctx->dest_id);
silc_free(ctx);
- win->sock->protocol = NULL;
+ conn->sock->protocol = NULL;
}
/* Internal routine that sends packet or marks packet to be sent. This
{
SilcClient client = (SilcClient)context;
SilcSocketConnection sock = NULL;
- SilcClientWindow win;
+ SilcClientConnection conn;
int ret;
SILC_LOG_DEBUG(("Processing packet"));
if (sock == NULL)
return;
- win = (SilcClientWindow)sock->user_data;
+ conn = (SilcClientConnection)sock->user_data;
/* Packet sending */
if (type == SILC_TASK_WRITE) {
close the connection */
if (SILC_IS_DISCONNECTING(sock)) {
silc_client_close_connection(client, sock);
+ client->ops->disconnect(client, conn);
return;
}
- silc_say(client, "Connection closed: premature EOF");
+ client->ops->say(client, conn, "Connection closed: premature EOF");
SILC_LOG_DEBUG(("Premature EOF from connection %d", sock->sock));
-
silc_client_close_connection(client, sock);
+ client->ops->disconnect(client, conn);
return;
}
/* Process the packet. This will call the parser that will then
decrypt and parse the packet. */
- if (!silc_packet_receive_process(sock, win->receive_key, win->hmac,
+ if (!silc_packet_receive_process(sock, conn->receive_key, conn->hmac,
silc_client_packet_parse, client)) {
silc_buffer_clear(sock->inbuf);
return;
SilcPacketContext *packet = parse_ctx->packet;
SilcBuffer buffer = packet->buffer;
SilcSocketConnection sock = parse_ctx->sock;
- SilcClientWindow win = (SilcClientWindow)sock->user_data;
+ SilcClientConnection conn = (SilcClientConnection)sock->user_data;
int ret;
SILC_LOG_DEBUG(("Start"));
/* Decrypt the received packet */
- ret = silc_packet_decrypt(win->receive_key, win->hmac, buffer, packet);
+ ret = silc_packet_decrypt(conn->receive_key, conn->hmac, buffer, packet);
if (ret < 0)
goto out;
void silc_client_packet_parse(SilcPacketParserContext *parser_context)
{
SilcClient client = (SilcClient)parser_context->context;
- SilcClientWindow win = (SilcClientWindow)parser_context->sock->user_data;
-
- /* If this packet is for the current active connection we will
- parse the packet right away to get it quickly on the screen.
- Otherwise, it will be parsed with a timeout as the data is
- for inactive window (which might not be visible at all). */
- if (SILC_CLIENT_IS_CURRENT_WIN(client, win)) {
- /* Parse it real soon */
- silc_task_register(client->timeout_queue, parser_context->sock->sock,
- silc_client_packet_parse_real,
- (void *)parser_context, 0, 1,
- SILC_TASK_TIMEOUT,
- SILC_TASK_PRI_NORMAL);
- } else {
- /* Parse the packet with timeout */
- silc_task_register(client->timeout_queue, parser_context->sock->sock,
- silc_client_packet_parse_real,
- (void *)parser_context, 0, 200000,
- SILC_TASK_TIMEOUT,
- SILC_TASK_PRI_NORMAL);
- }
+
+ /* Parse the packet */
+ silc_task_register(client->timeout_queue, parser_context->sock->sock,
+ silc_client_packet_parse_real,
+ (void *)parser_context, 0, 1,
+ SILC_TASK_TIMEOUT,
+ SILC_TASK_PRI_NORMAL);
}
/* Parses the packet type and calls what ever routines the packet type
/*
* Received private message
*/
- {
- SilcClientCommandReplyContext ctx;
- ctx = silc_calloc(1, sizeof(*ctx));
- ctx->client = client;
- ctx->sock = sock;
- ctx->packet = packet;
- silc_client_command_reply_msg((void *)ctx);
- }
+ silc_client_private_message(client, sock, packet);
break;
case SILC_PACKET_PRIVATE_MESSAGE_KEY:
/*
/* Get data used in the packet sending, keys and stuff */
if ((!cipher || !hmac || !dst_id) && sock->user_data) {
- if (!cipher && ((SilcClientWindow)sock->user_data)->send_key)
- cipher = ((SilcClientWindow)sock->user_data)->send_key;
+ if (!cipher && ((SilcClientConnection)sock->user_data)->send_key)
+ cipher = ((SilcClientConnection)sock->user_data)->send_key;
- if (!hmac && ((SilcClientWindow)sock->user_data)->hmac)
- hmac = ((SilcClientWindow)sock->user_data)->hmac;
+ if (!hmac && ((SilcClientConnection)sock->user_data)->hmac)
+ hmac = ((SilcClientConnection)sock->user_data)->hmac;
- if (!dst_id && ((SilcClientWindow)sock->user_data)->remote_id) {
- dst_id = ((SilcClientWindow)sock->user_data)->remote_id;
+ if (!dst_id && ((SilcClientConnection)sock->user_data)->remote_id) {
+ dst_id = ((SilcClientConnection)sock->user_data)->remote_id;
dst_id_type = SILC_ID_SERVER;
}
}
/* Set the packet context pointers */
packetdata.flags = 0;
packetdata.type = type;
- if (((SilcClientWindow)sock->user_data)->local_id_data)
- packetdata.src_id = ((SilcClientWindow)sock->user_data)->local_id_data;
+ if (((SilcClientConnection)sock->user_data)->local_id_data)
+ packetdata.src_id = ((SilcClientConnection)sock->user_data)->local_id_data;
else
packetdata.src_id = silc_calloc(SILC_ID_CLIENT_LEN, sizeof(unsigned char));
packetdata.src_id_len = SILC_ID_CLIENT_LEN;
int force_send)
{
int i;
- SilcClientWindow win = (SilcClientWindow)sock->user_data;
+ SilcClientConnection conn = (SilcClientConnection)sock->user_data;
SilcBuffer payload;
SilcPacketContext packetdata;
SilcCipher cipher;
SILC_LOG_DEBUG(("Sending packet to channel"));
if (!channel || !channel->key) {
- silc_say(client, "Cannot talk to channel: key does not exist");
+ client->ops->say(client, conn,
+ "Cannot talk to channel: key does not exist");
return;
}
silc_hash_make(client->md5hash, channel->iv, 16, channel->iv);
/* Encode the channel payload */
- payload = silc_channel_encode_payload(strlen(win->nickname), win->nickname,
+ payload = silc_channel_encode_payload(strlen(conn->nickname), conn->nickname,
data_len, data, 16, channel->iv,
client->rng);
if (!payload) {
- silc_say(client,
- "Error: Could not create packet to be sent to the channel");
+ client->ops->say(client, conn,
+ "Error: Could not create packet to be sent to channel");
return;
}
/* Get data used in packet header encryption, keys and stuff. Rest
of the packet (the payload) is, however, encrypted with the
specified channel key. */
- cipher = win->send_key;
- hmac = win->hmac;
+ cipher = conn->send_key;
+ hmac = conn->hmac;
id_string = silc_id_id2str(channel->id, SILC_ID_CHANNEL);
/* Set the packet context pointers. The destination ID is always
distribution of the packet. */
packetdata.flags = 0;
packetdata.type = SILC_PACKET_CHANNEL_MESSAGE;
- packetdata.src_id = win->local_id_data;
+ packetdata.src_id = conn->local_id_data;
packetdata.src_id_len = SILC_ID_CLIENT_LEN;
packetdata.src_id_type = SILC_ID_CLIENT;
packetdata.dst_id = id_string;
unsigned int data_len,
int force_send)
{
- SilcClientWindow win = (SilcClientWindow)sock->user_data;
+ SilcClientConnection conn = (SilcClientConnection)sock->user_data;
SilcBuffer buffer;
SilcPacketContext packetdata;
unsigned int nick_len;
SILC_LOG_DEBUG(("Sending private message"));
/* Create private message payload */
- nick_len = strlen(client->current_win->nickname);
+ nick_len = strlen(conn->nickname);
buffer = silc_buffer_alloc(2 + nick_len + data_len);
silc_buffer_pull_tail(buffer, SILC_BUFFER_END(buffer));
silc_buffer_format(buffer,
SILC_STR_UI_SHORT(nick_len),
- SILC_STR_UI_XNSTRING(client->current_win->nickname,
+ SILC_STR_UI_XNSTRING(conn->nickname,
nick_len),
SILC_STR_UI_XNSTRING(data, data_len),
SILC_STR_END);
/* Get data used in the encryption */
cipher = client_entry->send_key;
- hmac = win->hmac;
+ hmac = conn->hmac;
/* Set the packet context pointers. */
packetdata.flags = 0;
packetdata.type = SILC_PACKET_PRIVATE_MESSAGE;
- packetdata.src_id = win->local_id_data;
+ packetdata.src_id = conn->local_id_data;
packetdata.src_id_len = SILC_ID_CLIENT_LEN;
packetdata.src_id_type = SILC_ID_CLIENT;
if (client_entry)
packetdata.dst_id = silc_id_id2str(client_entry->id, SILC_ID_CLIENT);
else
- packetdata.dst_id = win->local_id_data;
+ packetdata.dst_id = conn->local_id_data;
packetdata.dst_id_len = SILC_ID_CLIENT_LEN;
packetdata.dst_id_type = SILC_ID_CLIENT;
packetdata.rng = client->rng;
void silc_client_close_connection(SilcClient client,
SilcSocketConnection sock)
{
- SilcClientWindow win;
+ SilcClientConnection conn;
/* We won't listen for this connection anymore */
silc_schedule_unset_listen_fd(sock->sock);
/* Close the actual connection */
silc_net_close_connection(sock->sock);
- silc_say(client, "Closed connection to host %s", sock->hostname ?
- sock->hostname : sock->ip);
+ client->ops->say(client, sock->user_data,
+ "Closed connection to host %s", sock->hostname ?
+ sock->hostname : sock->ip);
/* Free everything */
if (sock->user_data) {
- win = (SilcClientWindow)sock->user_data;
+ conn = (SilcClientConnection)sock->user_data;
/* XXX Free all client entries and channel entries. */
/* Clear ID caches */
- silc_idcache_del_all(win->client_cache);
- silc_idcache_del_all(win->channel_cache);
+ silc_idcache_del_all(conn->client_cache);
+ silc_idcache_del_all(conn->channel_cache);
/* Free data */
- if (win->remote_host)
- silc_free(win->remote_host);
- if (win->local_id)
- silc_free(win->local_id);
- if (win->local_id_data)
- silc_free(win->local_id_data);
- if (win->send_key)
- silc_cipher_free(win->send_key);
- if (win->receive_key)
- silc_cipher_free(win->receive_key);
- if (win->hmac)
- silc_hmac_free(win->hmac);
- if (win->hmac_key) {
- memset(win->hmac_key, 0, win->hmac_key_len);
- silc_free(win->hmac_key);
+ if (conn->remote_host)
+ silc_free(conn->remote_host);
+ if (conn->local_id)
+ silc_free(conn->local_id);
+ if (conn->local_id_data)
+ silc_free(conn->local_id_data);
+ if (conn->send_key)
+ silc_cipher_free(conn->send_key);
+ if (conn->receive_key)
+ silc_cipher_free(conn->receive_key);
+ if (conn->hmac)
+ silc_hmac_free(conn->hmac);
+ if (conn->hmac_key) {
+ memset(conn->hmac_key, 0, conn->hmac_key_len);
+ silc_free(conn->hmac_key);
}
- win->sock = NULL;
- win->remote_port = 0;
- win->remote_type = 0;
- win->send_key = NULL;
- win->receive_key = NULL;
- win->hmac = NULL;
- win->hmac_key = NULL;
- win->hmac_key_len = 0;
- win->local_id = NULL;
- win->local_id_data = NULL;
- win->remote_host = NULL;
- win->current_channel = NULL;
+ conn->sock = NULL;
+ conn->remote_port = 0;
+ conn->remote_type = 0;
+ conn->send_key = NULL;
+ conn->receive_key = NULL;
+ conn->hmac = NULL;
+ conn->hmac_key = NULL;
+ conn->hmac_key_len = 0;
+ conn->local_id = NULL;
+ conn->local_id_data = NULL;
+ conn->remote_host = NULL;
+ conn->current_channel = NULL;
}
if (sock->protocol) {
msg = silc_calloc(message->len + 1, sizeof(char));
memcpy(msg, message->data, message->len);
- silc_say(client, msg);
+ client->ops->say(client, sock->user_data, msg);
silc_free(msg);
SILC_SET_DISCONNECTED(sock);
msg = silc_calloc(message->len + 1, sizeof(char));
memcpy(msg, message->data, message->len);
- silc_say(client, msg);
+ client->ops->say(client, sock->user_data, msg);
silc_free(msg);
}
msg = silc_calloc(message->len + 1, sizeof(char));
memcpy(msg, message->data, message->len);
- silc_say(client, msg);
+ client->ops->say(client, sock->user_data, msg);
silc_free(msg);
}
SilcSocketConnection sock,
unsigned char *id_string)
{
- SilcClientWindow win = (SilcClientWindow)sock->user_data;
+ SilcClientConnection conn = (SilcClientConnection)sock->user_data;
/* Delete old ID from ID cache */
- silc_idcache_del_by_id(win->client_cache, SILC_ID_CLIENT, win->local_id);
+ silc_idcache_del_by_id(conn->client_cache, SILC_ID_CLIENT, conn->local_id);
/* Save the new ID */
- if (win->local_id)
- silc_free(win->local_id);
- win->local_id = silc_id_str2id(id_string, SILC_ID_CLIENT);
- if (win->local_id_data)
- silc_free(win->local_id_data);
- win->local_id_data =
+ if (conn->local_id)
+ silc_free(conn->local_id);
+ conn->local_id = silc_id_str2id(id_string, SILC_ID_CLIENT);
+ if (conn->local_id_data)
+ silc_free(conn->local_id_data);
+ conn->local_id_data =
silc_calloc(SILC_ID_CLIENT_LEN, sizeof(unsigned char));
- memcpy(win->local_id_data, id_string, SILC_ID_CLIENT_LEN);
- win->local_id_data_len = SILC_ID_CLIENT_LEN;
- if (!win->local_entry)
- win->local_entry = silc_calloc(1, sizeof(*win->local_entry));
- win->local_entry->nickname = win->nickname;
- win->local_entry->id = win->local_id;
+ memcpy(conn->local_id_data, id_string, SILC_ID_CLIENT_LEN);
+ conn->local_id_data_len = SILC_ID_CLIENT_LEN;
+ if (!conn->local_entry)
+ conn->local_entry = silc_calloc(1, sizeof(*conn->local_entry));
+ conn->local_entry->nickname = conn->nickname;
+ conn->local_entry->id = conn->local_id;
/* Put it to the ID cache */
- silc_idcache_add(win->client_cache, win->nickname, SILC_ID_CLIENT,
- win->local_id, (void *)win->local_entry, TRUE);
+ silc_idcache_add(conn->client_cache, conn->nickname, SILC_ID_CLIENT,
+ conn->local_id, (void *)conn->local_entry, TRUE);
}
/* Processed received Channel ID for a channel. This is called when client
unsigned int mode,
unsigned char *id_string)
{
- SilcClientWindow win = (SilcClientWindow)sock->user_data;
+ SilcClientConnection conn = (SilcClientConnection)sock->user_data;
SilcChannelID *id;
SilcChannelEntry channel;
channel->channel_name = channel_name;
channel->id = id;
channel->mode = mode;
- win->current_channel = channel;
+ conn->current_channel = channel;
/* Put it to the ID cache */
- silc_idcache_add(win->channel_cache, channel_name, SILC_ID_CHANNEL,
+ silc_idcache_add(conn->channel_cache, channel_name, SILC_ID_CHANNEL,
(void *)id, (void *)channel, TRUE);
}
{
unsigned char *id_string, *key, *cipher;
unsigned int key_len;
- SilcClientWindow win = (SilcClientWindow)sock->user_data;
+ SilcClientConnection conn = (SilcClientConnection)sock->user_data;
SilcChannelID *id;
SilcIDCacheEntry id_cache = NULL;
SilcChannelEntry channel;
id = silc_id_str2id(id_string, SILC_ID_CHANNEL);
/* Find channel. */
- if (!silc_idcache_find_by_id_one(win->channel_cache, (void *)id,
+ if (!silc_idcache_find_by_id_one(conn->channel_cache, (void *)id,
SILC_ID_CHANNEL, &id_cache))
goto out;
silc_cipher_alloc(cipher, &channel->channel_key);
if (!channel->channel_key) {
- silc_say(client, "Cannot talk to channel: unsupported cipher %s", cipher);
+ client->ops->say(client, conn,
+ "Cannot talk to channel: unsupported cipher %s", cipher);
goto out;
}
channel->channel_key->cipher->set_key(channel->channel_key->context,
SilcSocketConnection sock,
SilcPacketContext *packet)
{
- SilcClientWindow win = (SilcClientWindow)sock->user_data;
+ SilcClientConnection conn = (SilcClientConnection)sock->user_data;
SilcBuffer buffer = packet->buffer;
SilcChannelPayload payload = NULL;
SilcChannelID *id = NULL;
id = silc_id_str2id(packet->dst_id, SILC_ID_CHANNEL);
/* Find the channel entry from channels on this window */
- if (!silc_idcache_find_by_id_one(win->channel_cache, (void *)id,
+ if (!silc_idcache_find_by_id_one(conn->channel_cache, (void *)id,
SILC_ID_CHANNEL, &id_cache))
goto out;
if (!payload)
goto out;
- /* Display the message on screen */
+ /* Pass the message to application */
if (packet->src_id_type == SILC_ID_CLIENT) {
- /* Message from client */
- if (channel == win->current_channel)
- silc_print(client, "<%s> %s",
- silc_channel_get_nickname(payload, NULL),
- silc_channel_get_data(payload, NULL));
- else
- silc_print(client, "<%s:%s> %s",
- silc_channel_get_nickname(payload, NULL),
- channel->channel_name,
- silc_channel_get_data(payload, NULL));
+ client->ops->channel_message(client, conn,
+ silc_channel_get_nickname(payload, NULL),
+ channel->channel_name,
+ silc_channel_get_data(payload, NULL));
} else {
/* Message from server */
- silc_say(client, "%s", silc_channel_get_data(payload, NULL));
+ /* XXX maybe this should be passed to app... */
+ client->ops->say(client, conn, "%s", silc_channel_get_data(payload, NULL));
}
out:
if (payload)
silc_channel_free_payload(payload);
}
+
+/* Private message received. This processes the private message and
+ finally displays it on the screen. */
+
+void silc_client_private_message(SilcClient client,
+ SilcSocketConnection sock,
+ SilcPacketContext *packet)
+{
+ SilcClientConnection conn = (SilcClientConnection)sock->user_data;
+ SilcBuffer buffer = 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);
+
+ /* Pass the private message to application */
+ client->ops->private_message(client, conn, nickname, message);
+
+ /* See if we are away (gone). If we are away we will reply to the
+ sender with the set away message. */
+ if (conn->away && conn->away->away) {
+ SilcClientID *remote_id;
+ SilcClientEntry remote_client;
+ SilcIDCacheEntry id_cache;
+
+ if (packet->src_id_type != SILC_ID_CLIENT)
+ goto out;
+
+ remote_id = silc_id_str2id(packet->src_id, SILC_ID_CLIENT);
+ if (!remote_id)
+ goto out;
+
+ if (!SILC_ID_CLIENT_COMPARE(remote_id, conn->local_id))
+ goto out;
+
+ /* Check whether we know this client already */
+ if (!silc_idcache_find_by_id_one(conn->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(conn->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, sock, remote_client,
+ conn->away->away,
+ strlen(conn->away->away), TRUE);
+ }
+
+ out:
+ memset(message, 0, buffer->len);
+ silc_free(message);
+ silc_free(nickname);
+}