Created SILC Client Libary by moving stuff from silc/ directory.
[silc.git] / lib / silcclient / client.c
similarity index 58%
rename from apps/silc/client.c
rename to lib/silcclient/client.c
index b3a6d391bc0c38ac1e3f220e51bea94256cbacf4..d2b4aa660420eafa56ffaf918292651712fdcd9f 100644 (file)
   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 */
@@ -136,18 +64,7 @@ void silc_client_free(SilcClient client)
 
 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);
@@ -161,84 +78,14 @@ int silc_client_init(SilcClient client)
   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
@@ -256,7 +103,7 @@ void silc_client_stop(SilcClient client)
 
   silc_client_protocols_unregister();
 
-  SILC_LOG_DEBUG(("Client client"));
+  SILC_LOG_DEBUG(("Client stopped"));
 }
 
 /* Runs the client. */
@@ -270,457 +117,60 @@ void silc_client_run(SilcClient 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;
@@ -755,25 +205,32 @@ silc_client_connect_to_server_internal(SilcClientInternalConnectContext *ctx)
   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;
@@ -790,6 +247,7 @@ SILC_TASK_CALLBACK(silc_client_connect_to_server_start)
   SilcClientInternalConnectContext *ctx =
     (SilcClientInternalConnectContext *)context;
   SilcClient client = ctx->client;
+  SilcClientConnection conn = ctx->conn;
   SilcProtocol protocol;
   SilcClientKEInternalContext *proto_ctx;
   int opt, opt_len = sizeof(opt);
@@ -801,10 +259,11 @@ SILC_TASK_CALLBACK(silc_client_connect_to_server_start)
   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);
@@ -816,12 +275,15 @@ SILC_TASK_CALLBACK(silc_client_connect_to_server_start)
       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;
   }
@@ -831,22 +293,24 @@ SILC_TASK_CALLBACK(silc_client_connect_to_server_start)
   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;
 
@@ -856,10 +320,12 @@ SILC_TASK_CALLBACK(silc_client_connect_to_server_start)
                      &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 
@@ -898,6 +364,9 @@ SILC_TASK_CALLBACK(silc_client_connect_to_server_second)
       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;
   }
 
@@ -911,29 +380,14 @@ SILC_TASK_CALLBACK(silc_client_connect_to_server_second)
   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);
@@ -966,7 +420,7 @@ SILC_TASK_CALLBACK(silc_client_connect_to_server_final)
   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"));
@@ -982,7 +436,10 @@ SILC_TASK_CALLBACK(silc_client_connect_to_server_final)
     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;
   }
 
@@ -1008,15 +465,15 @@ SILC_TASK_CALLBACK(silc_client_connect_to_server_final)
   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)
@@ -1026,7 +483,7 @@ SILC_TASK_CALLBACK(silc_client_connect_to_server_final)
   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
@@ -1065,7 +522,7 @@ SILC_TASK_CALLBACK(silc_client_packet_process)
 {
   SilcClient client = (SilcClient)context;
   SilcSocketConnection sock = NULL;
-  SilcClientWindow win;
+  SilcClientConnection conn;
   int ret;
 
   SILC_LOG_DEBUG(("Processing packet"));
@@ -1074,7 +531,7 @@ SILC_TASK_CALLBACK(silc_client_packet_process)
   if (sock == NULL)
     return;
 
-  win = (SilcClientWindow)sock->user_data;
+  conn = (SilcClientConnection)sock->user_data;
 
   /* Packet sending */
   if (type == SILC_TASK_WRITE) {
@@ -1119,19 +576,20 @@ SILC_TASK_CALLBACK(silc_client_packet_process)
         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;
@@ -1148,13 +606,13 @@ SILC_TASK_CALLBACK(silc_client_packet_parse_real)
   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;
 
@@ -1185,27 +643,13 @@ SILC_TASK_CALLBACK(silc_client_packet_parse_real)
 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
@@ -1284,14 +728,7 @@ void silc_client_packet_parse_type(SilcClient client,
     /*
      * 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:
     /*
@@ -1408,14 +845,14 @@ void silc_client_packet_send(SilcClient client,
 
   /* 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;
     }
   }
@@ -1423,8 +860,8 @@ void silc_client_packet_send(SilcClient client,
   /* 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;
@@ -1487,7 +924,7 @@ void silc_client_packet_send_to_channel(SilcClient client,
                                        int force_send)
 {
   int i;
-  SilcClientWindow win = (SilcClientWindow)sock->user_data;
+  SilcClientConnection conn = (SilcClientConnection)sock->user_data;
   SilcBuffer payload;
   SilcPacketContext packetdata;
   SilcCipher cipher;
@@ -1497,7 +934,8 @@ void silc_client_packet_send_to_channel(SilcClient client,
   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;
   }
 
@@ -1509,20 +947,20 @@ void silc_client_packet_send_to_channel(SilcClient client,
     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
@@ -1530,7 +968,7 @@ void silc_client_packet_send_to_channel(SilcClient client,
      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;
@@ -1594,7 +1032,7 @@ void silc_client_packet_send_private_message(SilcClient client,
                                             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;
@@ -1604,12 +1042,12 @@ void silc_client_packet_send_private_message(SilcClient client,
   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);
@@ -1629,18 +1067,18 @@ void silc_client_packet_send_private_message(SilcClient client,
 
   /* 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;
@@ -1693,7 +1131,7 @@ void silc_client_packet_send_private_message(SilcClient client,
 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);
@@ -1705,49 +1143,50 @@ void silc_client_close_connection(SilcClient client,
   /* 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) {
@@ -1771,7 +1210,7 @@ void silc_client_disconnected_by_server(SilcClient client,
 
   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);
@@ -1789,7 +1228,7 @@ void silc_client_error_by_server(SilcClient client,
 
   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);
 }
 
@@ -1803,7 +1242,7 @@ void silc_client_notify_by_server(SilcClient client,
 
   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);
 }
 
@@ -1814,29 +1253,29 @@ void silc_client_receive_new_id(SilcClient client,
                                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
@@ -1848,7 +1287,7 @@ void silc_client_new_channel_id(SilcClient client,
                                unsigned int mode,
                                unsigned char *id_string)
 {
-  SilcClientWindow win = (SilcClientWindow)sock->user_data;
+  SilcClientConnection conn = (SilcClientConnection)sock->user_data;
   SilcChannelID *id;
   SilcChannelEntry channel;
 
@@ -1859,10 +1298,10 @@ void silc_client_new_channel_id(SilcClient client,
   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);
 }
 
@@ -1878,7 +1317,7 @@ void silc_client_receive_channel_key(SilcClient client,
 {
   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;
@@ -1898,7 +1337,7 @@ void silc_client_receive_channel_key(SilcClient client,
   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;
   
@@ -1913,7 +1352,8 @@ void silc_client_receive_channel_key(SilcClient client,
 
   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, 
@@ -1935,7 +1375,7 @@ void silc_client_channel_message(SilcClient client,
                                 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;
@@ -1949,7 +1389,7 @@ void silc_client_channel_message(SilcClient client,
   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;
 
@@ -1968,21 +1408,16 @@ void silc_client_channel_message(SilcClient client,
   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:
@@ -1991,3 +1426,74 @@ void silc_client_channel_message(SilcClient client,
   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);
+}