Rewrote file transfer.
authorPekka Riikonen <priikone@silcnet.org>
Wed, 14 Feb 2007 14:52:22 +0000 (14:52 +0000)
committerPekka Riikonen <priikone@silcnet.org>
Wed, 14 Feb 2007 14:52:22 +0000 (14:52 +0000)
Added TCP/UDP listener (internal) API.
Other small fixes.

20 files changed:
lib/silcclient/Makefile.ad
lib/silcclient/client.c
lib/silcclient/client.h
lib/silcclient/client_channel.c
lib/silcclient/client_connect.c
lib/silcclient/client_entry.c
lib/silcclient/client_entry.h
lib/silcclient/client_ftp.c
lib/silcclient/client_ftp.h [new file with mode: 0644]
lib/silcclient/client_internal.h
lib/silcclient/client_keyagr.c
lib/silcclient/client_listener.c [new file with mode: 0644]
lib/silcclient/client_listener.h [new file with mode: 0644]
lib/silcclient/client_notify.c
lib/silcclient/client_ops_example.c
lib/silcclient/client_resume.c [deleted file]
lib/silcclient/command.c
lib/silcclient/command_reply.c
lib/silcclient/silcclient.h
lib/silcclient/silcclient_entry.h

index da702bbabb49c1a95895bae188d97dc59f49f505..3d1195000e488178bf3c95717b0fad1be7dcf124 100644 (file)
@@ -3,7 +3,7 @@
 #
 #  Author: Pekka Riikonen <priikone@silcnet.org>
 #
-#  Copyright (C) 2000 - 2006 Pekka Riikonen
+#  Copyright (C) 2000 - 2007 Pekka Riikonen
 #
 #  This program is free software; you can redistribute it and/or modify
 #  it under the terms of the GNU General Public License as published by
@@ -29,12 +29,16 @@ libsilcclient_la_SOURCES =          \
        client_notify.c                 \
        client_attrs.c                  \
        client_keyagr.c                 \
+       client_listener.c               \
+       client_ftp.c                    \
        command.c                       \
        command_reply.c
 
 #ifdef SILC_DIST_TOOLKIT
-include_HEADERS=       \
-       silcclient.h
+include_HEADERS=               \
+       client.h                \
+       silcclient.h            \
+       silcclient_entry.h
 
 SILC_EXTRA_DIST = client_ops_example.c
 #endif SILC_DIST_TOOLKIT
index 72a4800fb1b591338e257251d7a9c00562d3cd5b..0ee9f52a78423ed50af27787ea2e6fea0c409fe4 100644 (file)
@@ -305,7 +305,7 @@ SILC_FSM_STATE(silc_client_connection_st_packet)
 
   case SILC_PACKET_FTP:
     /* File transfer packet */
-//    silc_fsm_next(fsm, silc_client_ftp);
+    silc_fsm_next(fsm, silc_client_ftp);
     break;
 
   case SILC_PACKET_CHANNEL_KEY:
@@ -564,9 +564,10 @@ SILC_FSM_STATE(silc_client_st_stop)
 
 /* Adds new connection.  Creates the connection context and returns it. */
 
-static SilcClientConnection
+SilcClientConnection
 silc_client_add_connection(SilcClient client,
                           SilcConnectionType conn_type,
+                          SilcBool connect,
                           SilcClientConnectionParams *params,
                           SilcPublicKey public_key,
                           SilcPrivateKey private_key,
@@ -626,31 +627,33 @@ silc_client_add_connection(SilcClient client,
   silc_list_init(conn->internal->thread_pool, SilcFSMThreadStruct, next);
 
   /* Allocate client, channel and serve caches */
-  conn->internal->client_cache = silc_idcache_alloc(0, SILC_ID_CLIENT,
-                                                   NULL, NULL);
-  conn->internal->channel_cache = silc_idcache_alloc(0, SILC_ID_CHANNEL,
-                                                    NULL, NULL);
-  conn->internal->server_cache = silc_idcache_alloc(0, SILC_ID_SERVER,
-                                                   NULL, NULL);
-  if (!conn->internal->client_cache || !conn->internal->channel_cache ||
-      !conn->internal->server_cache) {
-    silc_client_del_connection(client, conn);
-    return NULL;
+  if (conn_type != SILC_CONN_CLIENT) {
+    conn->internal->client_cache = silc_idcache_alloc(0, SILC_ID_CLIENT,
+                                                     NULL, NULL);
+    conn->internal->channel_cache = silc_idcache_alloc(0, SILC_ID_CHANNEL,
+                                                      NULL, NULL);
+    conn->internal->server_cache = silc_idcache_alloc(0, SILC_ID_SERVER,
+                                                     NULL, NULL);
+    if (!conn->internal->client_cache || !conn->internal->channel_cache ||
+       !conn->internal->server_cache) {
+      silc_client_del_connection(client, conn);
+      return NULL;
+    }
   }
 
-  //  conn->internal->ftp_sessions = silc_dlist_init();
-
-  /* Initialize our async operation so that application may abort us
-     while we're connecting. */
-  conn->internal->cop = silc_async_alloc(silc_client_connect_abort,
-                                        NULL, conn);
-  if (!conn->internal->cop) {
-    silc_client_del_connection(client, conn);
-    return NULL;
+  if (connect) {
+    /* Initialize our async operation so that application may abort us
+       while we're connecting. */
+    conn->internal->cop = silc_async_alloc(silc_client_connect_abort,
+                                          NULL, conn);
+    if (!conn->internal->cop) {
+      silc_client_del_connection(client, conn);
+      return NULL;
+    }
   }
 
-  /* Run the connection state machine.  If threads are in use the machine
-     is always run in a real thread. */
+  /* Run the connection state machine.  If threads are in use the connection
+     machine is always run in a real thread. */
   thread = silc_fsm_thread_alloc(&client->internal->fsm, conn,
                                 silc_client_connection_finished, NULL,
                                 client->internal->params->threads);
@@ -681,22 +684,28 @@ void silc_client_del_connection(SilcClient client, SilcClientConnection conn)
   silc_schedule_task_del_by_context(conn->internal->schedule, conn);
 
   /* Free all cache entries */
-  if (silc_idcache_get_all(conn->internal->server_cache, &list)) {
-    silc_list_start(list);
-    while ((entry = silc_list_get(list)))
-      silc_client_del_server(client, conn, entry->context);
+  if (conn->internal->server_cache) {
+    if (silc_idcache_get_all(conn->internal->server_cache, &list)) {
+      silc_list_start(list);
+      while ((entry = silc_list_get(list)))
+       silc_client_del_server(client, conn, entry->context);
+    }
   }
-  if (silc_idcache_get_all(conn->internal->channel_cache, &list)) {
-    silc_list_start(list);
-    while ((entry = silc_list_get(list))) {
-      silc_client_empty_channel(client, conn, entry->context);
-      silc_client_del_channel(client, conn, entry->context);
+  if (conn->internal->channel_cache) {
+    if (silc_idcache_get_all(conn->internal->channel_cache, &list)) {
+      silc_list_start(list);
+      while ((entry = silc_list_get(list))) {
+       silc_client_empty_channel(client, conn, entry->context);
+       silc_client_del_channel(client, conn, entry->context);
+      }
     }
   }
-  if (silc_idcache_get_all(conn->internal->client_cache, &list)) {
-    silc_list_start(list);
-    while ((entry = silc_list_get(list)))
-      silc_client_del_client(client, conn, entry->context);
+  if (conn->internal->client_cache) {
+    if (silc_idcache_get_all(conn->internal->client_cache, &list)) {
+      silc_list_start(list);
+      while ((entry = silc_list_get(list)))
+       silc_client_del_client(client, conn, entry->context);
+    }
   }
 
   /* Free ID caches */
@@ -750,7 +759,7 @@ silc_client_connect_to_server(SilcClient client,
     return NULL;
 
   /* Add new connection */
-  conn = silc_client_add_connection(client, SILC_CONN_SERVER, params,
+  conn = silc_client_add_connection(client, SILC_CONN_SERVER, TRUE, params,
                                    public_key, private_key, remote_host,
                                    port, callback, context);
   if (!conn) {
@@ -786,8 +795,11 @@ silc_client_connect_to_client(SilcClient client,
   if (!client || !remote_host)
     return NULL;
 
+  if (params)
+    params->no_authentication = TRUE;
+
   /* Add new connection */
-  conn = silc_client_add_connection(client, SILC_CONN_CLIENT, params,
+  conn = silc_client_add_connection(client, SILC_CONN_CLIENT, TRUE, params,
                                    public_key, private_key, remote_host,
                                    port, callback, context);
   if (!conn) {
@@ -829,7 +841,7 @@ silc_client_key_exchange(SilcClient client,
   }
 
   /* Add new connection */
-  conn = silc_client_add_connection(client, conn_type, params,
+  conn = silc_client_add_connection(client, conn_type, TRUE, params,
                                    public_key, private_key,
                                    (char *)host, port, callback, context);
   if (!conn) {
@@ -850,6 +862,13 @@ void silc_client_close_connection(SilcClient client,
 {
   SILC_LOG_DEBUG(("Closing connection %p", conn));
 
+  /* If connection machine is not running, we just delete the connection */
+  if (!silc_fsm_is_started(&conn->internal->fsm)) {
+    silc_packet_stream_destroy(conn->stream);
+    silc_client_del_connection(conn->client, conn);
+    return;
+  }
+
   /* Signal to close connection */
   conn->internal->status = SILC_CLIENT_CONN_DISCONNECTED;
   if (!conn->internal->disconnected) {
@@ -972,6 +991,10 @@ SilcBool silc_client_init(SilcClient client, const char *username,
   if (!username || !hostname || !realname)
     return FALSE;
 
+  client->internal->ftp_sessions = silc_dlist_init();
+  if (!client->internal->ftp_sessions)
+    return FALSE;
+
   if (!client->internal->params->dont_register_crypto_library) {
     /* Initialize the crypto library.  If application has done this already
        this has no effect.  Also, we will not be overriding something
index d84cc162815bd97a9026c50510bf2e2ded31f4b5..af1ff2081ee2cc14f2318aba1fb57fad4ddef758 100644 (file)
@@ -41,7 +41,6 @@ typedef struct SilcClientConnectionInternalStruct
      *SilcClientConnectionInternal;
 typedef struct SilcChannelPrivateKeyStruct *SilcChannelPrivateKey;
 
-
 /* Internal client entry context */
 typedef struct SilcClientEntryInternalStruct {
   SilcRwLock lock;             /* Read/write lock */
index 8f8772e1d36f51d530ca9466d88220ee6e97b6c1..554864320ddc24c53f1e3199218c6aed2a488d83 100644 (file)
@@ -873,8 +873,11 @@ SilcBool silc_client_channel_save_public_keys(SilcChannelEntry channel,
     return FALSE;
 
   if (!channel->channel_pubkeys) {
-    channel->channel_pubkeys = chpks;
-    return TRUE;
+    channel->channel_pubkeys = silc_dlist_init();
+    if (!channel->channel_pubkeys) {
+      silc_argument_list_free(chpks, SILC_ARGUMENT_PUBLIC_KEY);
+      return FALSE;
+    }
   }
 
   silc_dlist_start(chpks);
index ec7e81e50ed3aed892a4859256f737108fc5971a..bb3d74e84ad8a4e1a767bf93b6bd08d541f372c6 100644 (file)
@@ -232,7 +232,7 @@ static void silc_client_ke_completion(SilcSKE ske,
   SILC_LOG_DEBUG(("Key Exchange completed"));
 
   /* Key exchange done */
-  SILC_FSM_CALL_CONTINUE(fsm);
+  SILC_FSM_CALL_CONTINUE_SYNC(fsm);
 }
 
 /* Rekey protocol completion callback */
index 1cc1833709b635c20ad0e7bf1d9bbbbe4d158fbb..18546fb72909dfb2c792f8ed0a8bfc0665cba6f3 100644 (file)
@@ -903,11 +903,9 @@ void silc_client_del_client_entry(SilcClient client,
     silc_hmac_free(client_entry->internal.hmac_send);
   if (client_entry->internal.hmac_receive)
     silc_hmac_free(client_entry->internal.hmac_receive);
-#if 0
-  silc_client_ftp_session_free_client(conn, client_entry);
-  if (client_entry->internal->ke)
+  silc_client_ftp_session_free_client(client, client_entry);
+  if (client_entry->internal.ke)
     silc_client_abort_key_agreement(client, conn, client_entry);
-#endif /* 0 */
   silc_atomic_uninit8(&client_entry->internal.refcnt);
   silc_rwlock_free(client_entry->internal.lock);
   silc_free(client_entry);
@@ -1626,7 +1624,7 @@ void silc_client_lock_channel(SilcChannelEntry channel_entry)
   silc_rwlock_rdlock(channel_entry->internal.lock);
 }
 
-/* Unlock client */
+/* Unlock channel */
 
 void silc_client_unlock_channel(SilcChannelEntry channel_entry)
 {
index ee785f2d155b1f138e7ebb010e919199d7583ac2..7aef2860d98e433e226232a27de9d8c082838998 100644 (file)
@@ -74,5 +74,11 @@ SilcBool silc_client_nickname_parse(SilcClient client,
                                    SilcClientConnection conn,
                                    char *nickname,
                                    char **ret_nick);
+SilcUInt16 silc_client_get_clients_by_list(SilcClient client,
+                                          SilcClientConnection conn,
+                                          SilcUInt32 list_count,
+                                          SilcBuffer client_id_list,
+                                          SilcGetClientCallback completion,
+                                          void *context);
 
 #endif /* CLIENT_ENTRY_H */
index c3bbd177cb3adc87a0c61b312e51095630e4bdbf..5143c92177f445565283e28b5c0a7b8f6eb1d41a 100644 (file)
 #include "silcclient.h"
 #include "client_internal.h"
 
-static int
-silc_client_connect_to_client(SilcClient client,
-                             SilcClientConnection conn, int port,
-                             char *host, void *context);
-static int
-silc_client_connect_to_client_internal(SilcClientInternalConnectContext *ctx);
-SILC_TASK_CALLBACK(silc_client_ftp_connected);
-static void silc_client_ftp_start_key_agreement(SilcClientFtpSession session,
-                                               int sock);
+/************************** Types and definitions ***************************/
 
 /* File transmission session */
 struct SilcClientFtpSessionStruct {
-  SilcUInt32 session_id;
-  SilcClient client;
-  SilcClientConnection conn;
-  SilcClientEntry client_entry;
-
-  SilcSocketConnection sock;
-  SilcBuffer packet;
-
-  char *hostname;
-  SilcUInt16 port;
-  int listener;
-
-  SilcClientFileMonitor monitor;
+  SilcClient client;                 /* Client */
+  SilcClientConnection conn;         /* Connection to remote host */
+  SilcClientEntry client_entry;              /* The client entry */
+  SilcClientListener listener;       /* Listener */
+  SilcAsyncOperation op;             /* Operation for connecting */
+  SilcClientConnectionParams params;  /* Connection params */
+  SilcPublicKey public_key;          /* Public key used in key exchange */
+  SilcPrivateKey private_key;        /* Private key used in key exchange */
+  SilcUInt32 session_id;             /* File transfer ID */
+
+  SilcClientFileMonitor monitor;      /* File transfer monitor callback */
   void *monitor_context;
-  SilcClientFileAskName ask_name;
+  SilcClientFileAskName ask_name;     /* File name asking callback */
   void *ask_name_context;
-  char *filepath;
-  char *path;
-
-  SilcSFTP sftp;
-  SilcSFTPFilesystem fs;
-  unsigned int server : 1;     /* File sender sets this to TRUE */
-  unsigned int bound  : 1;
-
-  SilcSFTPHandle dir_handle;
-  SilcSFTPHandle read_handle;
-  SilcUInt64 filesize;
-  SilcUInt64 read_offset;
-  int fd;
+  char *filepath;                    /* The remote filename */
+  char *path;                        /* User given path to save the file  */
+
+  SilcStream stream;                 /* Wrapped SilcPacketStream */
+  SilcSFTP sftp;                     /* SFTP server/client */
+  SilcSFTPFilesystem fs;             /* SFTP memory file system */
+  SilcSFTPHandle dir_handle;         /* SFTP session directory handle */
+  SilcSFTPHandle read_handle;        /* SFTP session file handles */
+
+  char *hostname;                    /* Remote host */
+  SilcUInt16 port;                   /* Remote port */
+  SilcUInt64 filesize;               /* File size */
+  SilcUInt64 read_offset;            /* Current read offset */
+  int fd;                            /* File descriptor */
+  unsigned int initiator : 1;        /* File sender sets this to TRUE */
+  unsigned int closed    : 1;        /* silc_client_file_close called */
 };
 
-SILC_TASK_CALLBACK(silc_client_ftp_connected)
-{
-  SilcClientInternalConnectContext *ctx =
-    (SilcClientInternalConnectContext *)context;
-  SilcClient client = ctx->client;
-  SilcClientConnection conn = ctx->conn;
-  SilcClientFtpSession session = (SilcClientFtpSession)ctx->context;
-  int opt, opt_len = sizeof(opt);
-
-  SILC_LOG_DEBUG(("Start"));
-
-  /* Check the socket status as it might be in error */
-  silc_net_get_socket_opt(fd, SOL_SOCKET, SO_ERROR, &opt, &opt_len);
-  if (opt != 0) {
-    if (ctx->tries < 2) {
-      /* Connection failed but lets try again */
-      client->internal->ops->say(client, conn, SILC_CLIENT_MESSAGE_ERROR,
-                                "Could not connect to client %s: %s",
-                                ctx->host, strerror(opt));
-      client->internal->ops->say(client, conn, SILC_CLIENT_MESSAGE_AUDIT,
-                                "Connecting to port %d of client %s resumed",
-                                ctx->port, ctx->host);
-
-      /* Unregister old connection try */
-      silc_schedule_unset_listen_fd(client->schedule, fd);
-      silc_net_close_connection(fd);
-      silc_schedule_task_del(client->schedule, ctx->task);
-
-      /* Try again */
-      silc_client_connect_to_client_internal(ctx);
-      ctx->tries++;
-    } else {
-      /* Connection failed and we won't try anymore */
-      client->internal->ops->say(client, conn, SILC_CLIENT_MESSAGE_ERROR,
-                                "Could not connect to client %s: %s",
-                                ctx->host, strerror(opt));
-      silc_schedule_unset_listen_fd(client->schedule, fd);
-      silc_net_close_connection(fd);
-      silc_schedule_task_del(client->schedule, ctx->task);
-      silc_free(ctx);
-      silc_client_ftp_session_free(session);
-    }
-    return;
-  }
-
-  silc_schedule_unset_listen_fd(client->schedule, fd);
-  silc_schedule_task_del(client->schedule, ctx->task);
-
-  /* Start the key agreement */
-  silc_client_ftp_start_key_agreement(session, fd);
-}
-
-static int
-silc_client_connect_to_client_internal(SilcClientInternalConnectContext *ctx)
-{
-  int sock;
-
-  /* Create connection to server asynchronously */
-  sock = silc_net_create_connection_async(NULL, ctx->port, ctx->host);
-  if (sock < 0)
-    return -1;
-
-  /* Register task that will receive the async connect and will
-     read the result. */
-  ctx->task = silc_schedule_task_add(ctx->client->schedule, sock,
-                                    silc_client_ftp_connected,
-                                    (void *)ctx, 0, 0,
-                                    SILC_TASK_FD,
-                                    SILC_TASK_PRI_NORMAL);
-  silc_schedule_set_listen_fd(ctx->client->schedule, sock, SILC_TASK_WRITE,
-                             FALSE);
-  ctx->sock = sock;
-  return sock;
-}
-
-static int
-silc_client_connect_to_client(SilcClient client,
-                             SilcClientConnection conn, int port,
-                             char *host, void *context)
-{
-  SilcClientInternalConnectContext *ctx;
-
-  /* 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;
-  ctx->context = context;
-
-  /* Do the actual connecting process */
-  return silc_client_connect_to_client_internal(ctx);
-}
-
-/* SFTP packet send callback. This will use preallocated buffer to avoid
-   reallocation of outgoing data buffer everytime. */
-
-static void silc_client_ftp_send_packet(SilcBuffer packet, void *context)
-{
-  SilcClientFtpSession session = (SilcClientFtpSession)context;
-  SilcClient client = session->client;
-
-  SILC_LOG_DEBUG(("Start"));
-
-  /* Allocate outgoing packet */
-  if (!session->packet)
-    session->packet = silc_buffer_alloc(1 + packet->len);
-
-  /* Enlarge outgoing packet if needed */
-  if (session->packet->truelen < 1 + packet->len)
-    session->packet = silc_buffer_realloc(session->packet, 1 + packet->len);
-
-  /* Encode packet */
-  silc_buffer_pull_tail(session->packet, 1 + packet->len);
-  silc_buffer_format(session->packet,
-                    SILC_STR_UI_CHAR(1),
-                    SILC_STR_UI_XNSTRING(packet->data, packet->len),
-                    SILC_STR_END);
-
-  /* Send the packet immediately */
-  silc_client_packet_send(client, session->sock, SILC_PACKET_FTP, NULL,
-                         0, NULL, NULL, session->packet->data,
-                         session->packet->len, TRUE);
-
-  /* Clear buffer */
-  session->packet->data = session->packet->tail = session->packet->head;
-  session->packet->len = 0;
-}
+/************************* SFTP Server Callbacks ****************************/
 
 /* SFTP monitor callback for SFTP server. This reports the application
    how the transmission is going along. This function is for the client
@@ -219,6 +83,8 @@ static void silc_client_ftp_monitor(SilcSFTP sftp,
   }
 }
 
+/************************* SFTP Client Callbacks ****************************/
+
 /* Returns the read data. This is the downloader's function (client side)
    to receive the read data and read more until EOF is received from
    the other side. This will also monitor the transmission and notify
@@ -274,6 +140,9 @@ static void silc_client_ftp_data(SilcSFTP sftp,
                  SILC_PACKET_MAX_LEN - 1024,
                 silc_client_ftp_data, session);
 
+  /* Write the read data to the real file */
+  silc_file_write(session->fd, data, data_len);
+
   /* Call monitor callback */
   if (session->monitor)
     (*session->monitor)(session->client, session->conn,
@@ -282,9 +151,6 @@ static void silc_client_ftp_data(SilcSFTP sftp,
                        session->read_offset, session->filesize,
                        session->client_entry, session->session_id,
                        session->filepath, session->monitor_context);
-
-  /* Write the read data to the real file */
-  silc_file_write(session->fd, data, data_len);
 }
 
 /* Returns handle for the opened file. This is the downloader's function.
@@ -318,7 +184,7 @@ static void silc_client_ftp_open_handle(SilcSFTP sftp,
   /* Open the actual local file */
   memset(path, 0, sizeof(path));
   silc_snprintf(path, sizeof(path) - 1, "%s%s", session->path ?
-          session->path : "", session->filepath);
+               session->path : "", session->filepath);
   session->fd = silc_file_open(path, O_RDWR | O_CREAT | O_EXCL);
   if (session->fd < 0) {
     /* Call monitor callback */
@@ -354,6 +220,9 @@ static void silc_client_ftp_open_handle(SilcSFTP sftp,
                        session->filepath, session->monitor_context);
 }
 
+/* Ask filename completion callback.  Delivers the filepath selected by
+   user. */
+
 static void silc_client_ftp_ask_name(const char *filepath,
                                     void *context)
 {
@@ -493,324 +362,290 @@ static void silc_client_ftp_version(SilcSFTP sftp,
   silc_sftp_opendir(sftp, "", silc_client_ftp_opendir_handle, session);
 }
 
-/* This callback is called after the key agreement protocol has been
-   performed. This calls the final completion callback for the application. */
+/* SFTP stream error callback */
 
-SILC_TASK_CALLBACK(silc_client_ftp_key_agreement_final)
+static void silc_client_ftp_error(SilcSFTP sftp, SilcSFTPStatus status,
+                                 void *context)
 {
-  SilcProtocol protocol = (SilcProtocol)context;
-  SilcClientKEInternalContext *ctx =
-    (SilcClientKEInternalContext *)protocol->context;
-  SilcClientFtpSession session = (SilcClientFtpSession)ctx->context;
-  SilcClientConnection conn = (SilcClientConnection)ctx->sock->user_data;
 
-  SILC_LOG_DEBUG(("Start"));
+}
+
+/************************ Static utility functions **************************/
+
+/* Free session resources.   Connection must be closed before getting
+   here. */
+
+static void silc_client_ftp_session_free(SilcClientFtpSession session)
+{
+  SILC_LOG_DEBUG(("Free session %d", session->session_id));
+
+  silc_dlist_del(session->client->internal->ftp_sessions, session);
+
+  /* Abort connecting  */
+  if (session->op)
+    silc_async_abort(session->op, NULL, NULL);
+
+  /* Destroy SFTP */
+  if (session->sftp) {
+    if (session->initiator)
+      silc_sftp_server_shutdown(session->sftp);
+    else
+      silc_sftp_client_shutdown(session->sftp);
+  }
+  if (session->fs)
+    silc_sftp_fs_memory_free(session->fs);
+
+  /* Destroy listener */
+  if (session->listener)
+    silc_client_listener_free(session->listener);
+
+  /* Destroy wrapped stream */
+  if (session->stream)
+    silc_stream_destroy(session->stream);
+
+  silc_client_unref_client(session->client, session->conn,
+                          session->client_entry);
+  silc_free(session->hostname);
+  silc_free(session->filepath);
+  silc_free(session->path);
+  silc_free(session);
+}
+
+/* File transfer session timeout */
+
+SILC_TASK_CALLBACK(silc_client_ftp_timeout)
+{
+  SilcClientFtpSession session = context;
+
+  SILC_LOG_DEBUG(("Timeout"));
 
-  if (protocol->state == SILC_PROTOCOL_STATE_ERROR ||
-      protocol->state == SILC_PROTOCOL_STATE_FAILURE) {
+  /* Close connection (destroyes the session context later).  If it is
+     already closed, destroy the session now. */
+  if (session->conn) {
+    silc_client_close_connection(session->client, session->conn);
+    session->conn = NULL;
+  } else {
     /* Call monitor callback */
     if (session->monitor)
       (*session->monitor)(session->client, session->conn,
                          SILC_CLIENT_FILE_MONITOR_ERROR,
-                         SILC_CLIENT_FILE_KEY_AGREEMENT_FAILED, 0, 0,
+                         SILC_CLIENT_FILE_TIMEOUT, 0, 0,
                          session->client_entry, session->session_id,
                          session->filepath, session->monitor_context);
 
-    /* Error occured during protocol */
-    silc_ske_free_key_material(ctx->keymat);
-    goto out;
+    silc_client_ftp_session_free(context);
   }
+}
 
-  /* Set keys into use */
-  silc_client_protocol_ke_set_keys(ctx->ske, ctx->sock, ctx->keymat,
-                                  ctx->ske->prop->cipher,
-                                  ctx->ske->prop->pkcs,
-                                  ctx->ske->prop->hash,
-                                  ctx->ske->prop->hmac,
-                                  ctx->ske->prop->group,
-                                  ctx->responder);
-
-  if (!session->server) {
-    /* If we are the SFTP client then start the SFTP session and retrieve
-       the info about the file available for download. */
-    session->sftp = silc_sftp_client_start(silc_client_ftp_send_packet,
-                                          session, silc_client_ftp_version,
-                                          session);
-  } else {
-    /* Start SFTP server */
-    session->sftp = silc_sftp_server_start(silc_client_ftp_send_packet,
-                                          session, session->fs);
+/* File transfer session closing task callback */
 
-    /* Monitor transmission */
-    silc_sftp_server_set_monitor(session->sftp, SILC_SFTP_MONITOR_READ,
-                                silc_client_ftp_monitor, session);
+SILC_TASK_CALLBACK(silc_client_file_close_final)
+{
+  SilcClientFtpSession session = context;
+
+  /* Close connection (destroyes the session context later).  If it is
+     already closed, destroy the session now. */
+  if (session->conn) {
+    silc_client_close_connection(session->client, session->conn);
+    session->conn = NULL;
+  } else {
+    silc_client_ftp_session_free(context);
   }
+}
 
-  /* Set this as active session */
-  conn->internal->active_session = session;
+/* Client resolving callback.  Continues with the FTP packet processing */
 
- out:
-  silc_ske_free_key_material(ctx->keymat);
-  if (ctx->ske)
-    silc_ske_free(ctx->ske);
-  silc_free(ctx->dest_id);
-  ctx->sock->protocol = NULL;
-  silc_socket_free(ctx->sock);
-  silc_free(ctx);
-  silc_protocol_free(protocol);
+static void silc_client_ftp_client_resolved(SilcClient client,
+                                           SilcClientConnection conn,
+                                           SilcStatus status,
+                                           SilcDList clients,
+                                           void *context)
+{
+  SilcFSMThread thread = context;
+  SilcPacket packet = silc_fsm_get_state_context(thread);
+
+  /* If no client found, ignore the packet, a silent error */
+  if (!clients) {
+    silc_packet_free(packet);
+    silc_fsm_finish(thread);
+    return;
+  }
+
+  /* Continue processing the packet */
+  SILC_FSM_CALL_CONTINUE(context);
 }
 
-/* The downloader's function to start the key agreement protocol with the
-   remote client after we have connected to it. */
+/* FTP packet payload encoder/decoder.  This is called for every FTP packet.
+   We add/remove FTP payload in this function, because SFTP library does not
+   add/remove it. */
 
-static void silc_client_ftp_start_key_agreement(SilcClientFtpSession session,
-                                               int sock)
+static SilcBool
+silc_client_ftp_coder(SilcStream stream, SilcStreamStatus status,
+                     SilcBuffer buffer, void *context)
 {
-  SilcClient client = session->client;
-  SilcClientKEInternalContext *proto_ctx;
-  SilcProtocol protocol;
-  SilcClientConnection conn;
-  void *context;
+  /* Pull FTP type in the payload, revealing SFTP payload */
+  if (status == SILC_STREAM_CAN_READ) {
+    if (silc_buffer_len(buffer) >= 1)
+      silc_buffer_pull(buffer, 1);
+    return TRUE;
+  }
 
-  SILC_LOG_DEBUG(("Start"));
+  /* Add FTP type before SFTP data */
+  if (status == SILC_STREAM_CAN_WRITE) {
+    if (silc_buffer_format(buffer,
+                          SILC_STR_UI_CHAR(1),
+                          SILC_STR_END) < 0)
+      return FALSE;
+    return TRUE;
+  }
 
-  /* Call monitor callback */
-  if (session->monitor)
-    (*session->monitor)(session->client, session->conn,
-                       SILC_CLIENT_FILE_MONITOR_KEY_AGREEMENT,
-                       SILC_CLIENT_FILE_OK, 0, 0,
-                       session->client_entry, session->session_id,
-                       NULL, session->monitor_context);
-
-  /* Add new connection for this session */
-  conn = silc_client_add_connection(client, NULL, session->hostname,
-                                   session->port, session);
-
-  /* Allocate new socket connection object */
-  silc_socket_alloc(sock, SILC_SOCKET_TYPE_CLIENT, (void *)conn, &conn->sock);
-  conn->sock->hostname = strdup(session->hostname);
-  conn->sock->port = silc_net_get_remote_port(sock);
-  session->sock = silc_socket_dup(conn->sock);
-
-  /* Allocate internal context for key exchange protocol. This is
-     sent as context for the protocol. */
-  proto_ctx = silc_calloc(1, sizeof(*proto_ctx));
-  proto_ctx->client = client;
-  proto_ctx->sock = silc_socket_dup(conn->sock);
-  proto_ctx->rng = client->rng;
-  proto_ctx->responder = FALSE;
-  proto_ctx->context = session;
-  proto_ctx->send_packet = silc_client_protocol_ke_send_packet;
-  proto_ctx->verify = silc_client_protocol_ke_verify_key;
-
-  /* Perform key exchange protocol. */
-  silc_protocol_alloc(SILC_PROTOCOL_CLIENT_KEY_EXCHANGE,
-                     &protocol, (void *)proto_ctx,
-                     silc_client_ftp_key_agreement_final);
-  conn->sock->protocol = protocol;
-
-  /* Register the connection for network input and output. This sets
-     that scheduler will listen for incoming packets for this connection
-     and sets that outgoing packets may be sent to this connection as well.
-     However, this doesn't set the scheduler for outgoing traffic, it will
-     be set separately by calling SILC_CLIENT_SET_CONNECTION_FOR_OUTPUT,
-     later when outgoing data is available. */
-  context = (void *)client;
-  SILC_CLIENT_REGISTER_CONNECTION_FOR_IO(sock);
-
-  /* Execute the protocol */
-  silc_protocol_execute(protocol, client->schedule, 0, 0);
+  return FALSE;
 }
 
-/* The remote client's (the client who made the file available for download)
-   function for accepting incoming connection. This will also start the
-   key agreement protocol with the other client. */
+/* FTP Connection callback.  The SFTP session is started here. */
 
-SILC_TASK_CALLBACK(silc_client_ftp_process_key_agreement)
+static void
+silc_client_ftp_connect_completion(SilcClient client,
+                                  SilcClientConnection conn,
+                                  SilcClientConnectionStatus status,
+                                  SilcStatus error,
+                                  const char *message,
+                                  void *context)
 {
-  SilcClientFtpSession session = (SilcClientFtpSession)context;
-  SilcClient client = session->client;
-  SilcClientConnection conn;
-  SilcSocketConnection newsocket;
-  SilcClientKEInternalContext *proto_ctx;
-  int sock;
+  SilcClientFtpSession session = context;
 
-  SILC_LOG_DEBUG(("Start"));
+  session->conn = conn;
+  session->op = NULL;
+
+  silc_schedule_task_del_by_context(client->schedule, session);
+
+  switch (status) {
+  case SILC_CLIENT_CONN_SUCCESS:
+    SILC_LOG_DEBUG(("Connected, conn %p", conn));
+
+    /* Wrap the connection packet stream */
+    session->stream = silc_packet_stream_wrap(conn->stream, SILC_PACKET_FTP,
+                                             0, FALSE,
+                                             silc_client_ftp_coder, session);
+    if (!session->stream) {
+      /* Call monitor callback */
+      if (session->monitor)
+       (*session->monitor)(session->client, session->conn,
+                           SILC_CLIENT_FILE_MONITOR_ERROR,
+                           SILC_CLIENT_FILE_ERROR, 0, 0,
+                           session->client_entry, session->session_id,
+                           session->filepath, session->monitor_context);
+      silc_client_close_connection(client, conn);
+      session->conn = NULL;
+      return;
+    }
+
+    if (!session->initiator) {
+      /* If we are the SFTP client then start the SFTP session and retrieve
+        the info about the file available for download. */
+      session->sftp = silc_sftp_client_start(session->stream,
+                                            conn->internal->schedule,
+                                            silc_client_ftp_version,
+                                            silc_client_ftp_error, session);
+    } else {
+      /* Start SFTP server */
+      session->sftp = silc_sftp_server_start(session->stream,
+                                            conn->internal->schedule,
+                                            silc_client_ftp_error, session,
+                                            session->fs);
+
+      /* Monitor transmission */
+      silc_sftp_server_set_monitor(session->sftp, SILC_SFTP_MONITOR_READ,
+                                  silc_client_ftp_monitor, session);
+    }
+
+    break;
+
+  case SILC_CLIENT_CONN_DISCONNECTED:
+    SILC_LOG_DEBUG(("Disconnected %p", conn));
 
-  sock = silc_net_accept_connection(session->listener);
-  if (sock < 0) {
     /* Call monitor callback */
     if (session->monitor)
       (*session->monitor)(session->client, session->conn,
-                         SILC_CLIENT_FILE_MONITOR_ERROR,
+                         SILC_CLIENT_FILE_MONITOR_DISCONNECT,
                          SILC_CLIENT_FILE_ERROR, 0, 0,
                          session->client_entry, session->session_id,
                          session->filepath, session->monitor_context);
-    return;
-  }
 
-  /* Set socket options */
-  silc_net_set_socket_nonblock(sock);
-  silc_net_set_socket_opt(sock, SOL_SOCKET, SO_REUSEADDR, 1);
+    /* Connection already closed */
+    session->conn = NULL;
+
+    /* If closed by user, destroy the session now */
+    if (session->closed)
+      silc_client_ftp_session_free(session);
+    break;
 
-  /* Allocate new socket connection object */
-  silc_socket_alloc(sock, SILC_SOCKET_TYPE_CLIENT, NULL, &newsocket);
+  case SILC_CLIENT_CONN_ERROR_TIMEOUT:
+    SILC_LOG_DEBUG(("Connecting timeout"));
 
-  /* Perform name and address lookups for the remote host. */
-  silc_net_check_host_by_sock(sock, &newsocket->hostname, &newsocket->ip);
-  if (!newsocket->hostname && !newsocket->ip) {
     /* Call monitor callback */
     if (session->monitor)
       (*session->monitor)(session->client, session->conn,
                          SILC_CLIENT_FILE_MONITOR_ERROR,
-                         SILC_CLIENT_FILE_ERROR, 0, 0,
+                         SILC_CLIENT_FILE_TIMEOUT, 0, 0,
                          session->client_entry, session->session_id,
                          session->filepath, session->monitor_context);
-    return;
-  }
-  if (!newsocket->hostname)
-    newsocket->hostname = strdup(newsocket->ip);
-  newsocket->port = silc_net_get_remote_port(sock);
 
-  /* Call monitor callback */
-  if (session->monitor)
-    (*session->monitor)(session->client, session->conn,
-                       SILC_CLIENT_FILE_MONITOR_KEY_AGREEMENT,
-                       SILC_CLIENT_FILE_OK, 0, 0,
-                       session->client_entry, session->session_id,
-                       NULL, session->monitor_context);
-
-  /* Add new connection for this session */
-  conn = silc_client_add_connection(client, NULL, newsocket->hostname,
-                                   newsocket->port, session);
-  conn->sock = newsocket;
-  conn->sock->user_data = conn;
-  session->sock = silc_socket_dup(conn->sock);
-
-  /* Allocate internal context for key exchange protocol. This is
-     sent as context for the protocol. */
-  proto_ctx = silc_calloc(1, sizeof(*proto_ctx));
-  proto_ctx->client = client;
-  proto_ctx->sock = silc_socket_dup(conn->sock);
-  proto_ctx->rng = client->rng;
-  proto_ctx->responder = TRUE;
-  proto_ctx->context = session;
-  proto_ctx->send_packet = silc_client_protocol_ke_send_packet;
-  proto_ctx->verify = silc_client_protocol_ke_verify_key;
-
-  /* Prepare the connection for key exchange protocol. We allocate the
-     protocol but will not start it yet. The connector will be the
-     initiator of the protocol thus we will wait for initiation from
-     there before we start the protocol. */
-  silc_protocol_alloc(SILC_PROTOCOL_CLIENT_KEY_EXCHANGE,
-                     &newsocket->protocol, proto_ctx,
-                     silc_client_ftp_key_agreement_final);
-
-  /* Register the connection for network input and output. This sets
-     that scheduler will listen for incoming packets for this connection
-     and sets that outgoing packets may be sent to this connection as well.
-     However, this doesn't set the scheduler for outgoing traffic, it
-     will be set separately by calling SILC_CLIENT_SET_CONNECTION_FOR_OUTPUT,
-     later when outgoing data is available. */
-  context = (void *)client;
-  SILC_CLIENT_REGISTER_CONNECTION_FOR_IO(sock);
+    /* Connection already closed */
+    session->conn = NULL;
+    break;
+
+  default:
+    SILC_LOG_DEBUG(("Connecting error %d", status));
+
+    /* Call monitor callback */
+    if (session->monitor)
+      (*session->monitor)(session->client, session->conn,
+                         SILC_CLIENT_FILE_MONITOR_ERROR,
+                         status != SILC_CLIENT_CONN_ERROR ?
+                         SILC_CLIENT_FILE_KEY_AGREEMENT_FAILED :
+                         SILC_CLIENT_FILE_CONNECT_FAILED, 0, 0,
+                         session->client_entry, session->session_id,
+                         session->filepath, session->monitor_context);
+
+    /* Connection already closed */
+    session->conn = NULL;
+    break;
+  }
 }
 
+/*************************** File Transfer API ******************************/
+
 /* Free all file transfer sessions. */
 
-void silc_client_ftp_free_sessions(SilcClient client,
-                                  SilcClientConnection conn)
+void silc_client_ftp_free_sessions(SilcClient client)
 {
-  if (conn->internal->ftp_sessions) {
-    SilcClientFtpSession session;
-    silc_dlist_start(conn->internal->ftp_sessions);
-    while ((session = silc_dlist_get(conn->internal->ftp_sessions))
-          != SILC_LIST_END) {
-      if (session->sock)
-       session->sock->user_data = NULL;
-      silc_client_ftp_session_free(session);
-    }
-    silc_dlist_del(conn->internal->ftp_sessions, session);
-  }
+  SilcClientFtpSession session;
+
+  if (!client->internal->ftp_sessions)
+    return;
+
+  silc_dlist_start(client->internal->ftp_sessions);
+  while ((session = silc_dlist_get(client->internal->ftp_sessions)))
+    silc_client_ftp_session_free(session);
+  silc_dlist_del(client->internal->ftp_sessions, session);
 }
 
 /* Free file transfer session by client entry. */
 
-void silc_client_ftp_session_free_client(SilcClientConnection conn,
+void silc_client_ftp_session_free_client(SilcClient client,
                                         SilcClientEntry client_entry)
 {
   SilcClientFtpSession session;
 
-  if (!conn->internal->ftp_sessions)
+  if (!client->internal->ftp_sessions)
     return;
 
   /* Get the session */
-  silc_dlist_start(conn->internal->ftp_sessions);
-  while ((session = silc_dlist_get(conn->internal->ftp_sessions))
-        != SILC_LIST_END) {
+  silc_dlist_start(client->internal->ftp_sessions);
+  while ((session = silc_dlist_get(client->internal->ftp_sessions)))
     if (session->client_entry == client_entry)
       silc_client_ftp_session_free(session);
-  }
-}
-
-/* Free session resources. */
-
-void silc_client_ftp_session_free(SilcClientFtpSession session)
-{
-  SilcClientConnection conn;
-
-  SILC_LOG_DEBUG(("Free session"));
-
-  if (session->conn && session->conn->internal->ftp_sessions)
-    silc_dlist_del(session->conn->internal->ftp_sessions, session);
-
-  if (session->conn && session->conn->internal->active_session == session)
-    session->conn->internal->active_session = NULL;
-
-  if (session->sftp) {
-    if (session->server)
-      silc_sftp_server_shutdown(session->sftp);
-    else
-      silc_sftp_client_shutdown(session->sftp);
-  }
-
-  if (session->fs)
-    silc_sftp_fs_memory_free(session->fs);
-
-  /* Destroy listener */
-  if (session->listener) {
-    silc_schedule_unset_listen_fd(session->client->schedule,
-                                 session->listener);
-    silc_net_close_connection(session->listener);
-    silc_schedule_task_del_by_fd(session->client->schedule, session->listener);
-  }
-
-  /* Destroy session connection */
-  if (session->sock) {
-    silc_schedule_unset_listen_fd(session->client->schedule,
-                                 session->sock->sock);
-    silc_net_close_connection(session->sock->sock);
-
-    if (session->sock->user_data) {
-      conn = (SilcClientConnection)session->sock->user_data;
-
-      if (conn->internal->active_session == session)
-       conn->internal->active_session = NULL;
-
-      silc_client_close_connection_real(session->client, session->sock, conn);
-    } else {
-      silc_socket_free(session->sock);
-    }
-  }
-
-  if (session->packet)
-    silc_buffer_free(session->packet);
-
-  silc_free(session->hostname);
-  silc_free(session->filepath);
-  silc_free(session->path);
-  memset(session, 'F', sizeof(*session));
-  silc_free(session);
 }
 
 /* Sends a file indicated by the `filepath' to the remote client
@@ -821,6 +656,7 @@ void silc_client_ftp_session_free(SilcClientFtpSession session)
 
 SilcClientFileError
 silc_client_file_send(SilcClient client,
+                     SilcClientConnection conn,
                      SilcClientEntry client_entry,
                      SilcClientConnectionParams *params,
                      SilcPublicKey public_key,
@@ -831,13 +667,14 @@ silc_client_file_send(SilcClient client,
                      SilcUInt32 *session_id)
 {
   SilcClientFtpSession session;
-  SilcBuffer keyagr, ftp;
+  SilcBuffer keyagr;
   char *filename, *path;
   int fd;
 
-  SILC_LOG_DEBUG(("File send request (file: %s), filepath"));
+  SILC_LOG_DEBUG(("File send request (file: %s)", filepath));
 
-  if (!client || !client_entry || !filepath)
+  if (!client || !client_entry || !filepath || !params ||
+      !public_key || !private_key)
     return SILC_CLIENT_FILE_ERROR;
 
   /* Check for existing session for `filepath'. */
@@ -860,15 +697,19 @@ silc_client_file_send(SilcClient client,
     return SILC_CLIENT_FILE_ERROR;
   session->session_id = ++client->internal->next_session_id;
   session->client = client;
-  session->conn = conn;
-  session->server = TRUE;
-  session->client_entry = client_entry;
+  session->initiator = TRUE;
+  session->client_entry = silc_client_ref_client(client, conn, client_entry);
   session->monitor = monitor;
   session->monitor_context = monitor_context;
   session->filepath = strdup(filepath);
-  silc_dlist_add(conn->internal->ftp_sessions, session);
+  session->params = *params;
+  session->public_key = public_key;
+  session->private_key = private_key;
 
-  silc_asprintf(&path, "file://%s", filepath);
+  if (silc_asprintf(&path, "file://%s", filepath) < 0) {
+    silc_free(session);
+    return SILC_CLIENT_FILE_NO_MEMORY;
+  }
 
   /* Allocate memory filesystem and put the file to it */
   if (strrchr(path, '/'))
@@ -883,67 +724,57 @@ silc_client_file_send(SilcClient client,
   session->filesize = silc_file_size(filepath);
 
   /* If local IP is provided, create listener for incoming key exchange */
-  if (params && (params->local_ip || params->bind_ip)) {
-    ke = silc_calloc(1, sizeof(*ke));
-    if (!ke) {
-      completion(client, conn, client_entry, SILC_KEY_AGREEMENT_NO_MEMORY,
-                NULL, context);
-      return;
-    }
-
-    /* TCP listener */
+  if (params->local_ip || params->bind_ip) {
     session->listener =
-      silc_net_tcp_create_listener(params->bind_ip ?
-                                  (const char **)&params->bind_ip :
-                                  (const char **)&params->local_ip,
-                                  1, params->local_port, FALSE, FALSE,
-                                  conn->internal->schedule,
-                                  silc_client_tcp_accept,
-                                  client_entry);
+      silc_client_listener_add(client,
+                              conn->internal->schedule,
+                              params, public_key, private_key,
+                              silc_client_ftp_connect_completion,
+                              session);
     if (!session->listener) {
-      /* Could not create listener. Do the second best thing; send empty
-        key agreement packet and let the remote client provide the point
-        for the key exchange. */
-      SILC_LOG_DEBUG(("Could not create listener"));
-      silc_free(session->hostname);
-      session->hostname = NULL;
-      session->port = 0;
-    } else {
-      SILC_LOG_DEBUG(("Bound listener"));
-      session->bound = TRUE;
-      session->port = params->local_port;
-      if (!session->port) {
-       /* Get listener port */
-       SilcUInt16 *ports;
-       ports = silc_net_listener_get_port(ke->tcp_listener, NULL);
-       session->port = ports[0];
-       silc_free(ports);
-      }
+      client->internal->ops->say(client, conn, SILC_CLIENT_MESSAGE_ERROR,
+                                "Cannot create listener for file transfer: "
+                                "%s", strerror(errno));
+      silc_free(session);
+      return SILC_CLIENT_FILE_NO_MEMORY;
     }
+
+    session->hostname = (params->bind_ip ? strdup(params->bind_ip) :
+                        strdup(params->local_ip));
+    session->port = silc_client_listener_get_local_port(session->listener);
   }
 
   SILC_LOG_DEBUG(("Sending key agreement for file transfer"));
 
   /* Send the key agreement inside FTP packet */
-  keyagr = silc_key_agreement_payload_encode(session->hostname, session->port);
-
-  ftp = silc_buffer_alloc(1 + keyagr->len);
-  silc_buffer_pull_tail(ftp, SILC_BUFFER_END(ftp));
-  silc_buffer_format(ftp,
-                    SILC_STR_UI_CHAR(1),
-                    SILC_STR_UI_XNSTRING(keyagr->data, keyagr->len),
-                    SILC_STR_END);
-  silc_client_packet_send(client, conn->sock, SILC_PACKET_FTP,
-                         client_entry->id, SILC_ID_CLIENT, NULL, NULL,
-                         ftp->data, ftp->len, FALSE);
+  keyagr = silc_key_agreement_payload_encode(session->hostname, 0,
+                                            session->port);
+  if (!keyagr) {
+    if (session->listener)
+      silc_client_listener_free(session->listener);
+    silc_free(session);
+    return SILC_CLIENT_FILE_NO_MEMORY;
+  }
+  silc_packet_send_va_ext(conn->stream, SILC_PACKET_FTP, 0, 0, NULL,
+                         SILC_ID_CLIENT, &client_entry->id, NULL, NULL,
+                         SILC_STR_UI_CHAR(1),
+                         SILC_STR_DATA(silc_buffer_data(keyagr),
+                                       silc_buffer_len(keyagr)),
+                         SILC_STR_END);
 
   silc_buffer_free(keyagr);
-  silc_buffer_free(ftp);
   silc_free(path);
 
+  silc_dlist_add(client->internal->ftp_sessions, session);
   if (session_id)
     *session_id = session->session_id;
 
+  /* Add session request timeout */
+  if (params && params->timeout_secs)
+    silc_schedule_task_add_timeout(client->schedule,
+                                  silc_client_ftp_timeout, session,
+                                  params->timeout_secs, 0);
+
   return SILC_CLIENT_FILE_OK;
 }
 
@@ -957,6 +788,9 @@ silc_client_file_send(SilcClient client,
 SilcClientFileError
 silc_client_file_receive(SilcClient client,
                         SilcClientConnection conn,
+                        SilcClientConnectionParams *params,
+                        SilcPublicKey public_key,
+                        SilcPrivateKey private_key,
                         SilcClientFileMonitor monitor,
                         void *monitor_context,
                         const char *path,
@@ -965,15 +799,16 @@ silc_client_file_receive(SilcClient client,
                         void *ask_name_context)
 {
   SilcClientFtpSession session;
-  SilcBuffer keyagr, ftp;
+  SilcBuffer keyagr;
 
-  assert(client && conn);
+  if (!client || !conn)
+    return SILC_CLIENT_FILE_ERROR;
 
   SILC_LOG_DEBUG(("Start, Session ID: %d", session_id));
 
   /* Get the session */
-  silc_dlist_start(conn->internal->ftp_sessions);
-  while ((session = silc_dlist_get(conn->internal->ftp_sessions))
+  silc_dlist_start(client->internal->ftp_sessions);
+  while ((session = silc_dlist_get(client->internal->ftp_sessions))
         != SILC_LIST_END) {
     if (session->session_id == session_id) {
       break;
@@ -995,7 +830,6 @@ silc_client_file_receive(SilcClient client,
   session->monitor_context = monitor_context;
   session->ask_name = ask_name;
   session->ask_name_context = ask_name_context;
-  session->conn = conn;
   session->path = path ? strdup(path) : NULL;
 
   /* If the hostname and port already exists then the remote client did
@@ -1003,56 +837,67 @@ silc_client_file_receive(SilcClient client,
      create the connection ourselves. */
   if (session->hostname && session->port) {
     SILC_LOG_DEBUG(("Connecting to remote client"));
-    if (silc_client_connect_to_client(client, conn, session->port,
-                                     session->hostname, session) < 0)
+    /* Connect to the remote client.  Performs key exchange automatically. */
+    session->op =
+      silc_client_connect_to_client(client, params, public_key, private_key,
+                                   session->hostname, session->port,
+                                   silc_client_ftp_connect_completion,
+                                   session);
+    if (!session->op) {
+      silc_free(session);
       return SILC_CLIENT_FILE_ERROR;
+    }
   } else {
     /* Add the listener for the key agreement */
     SILC_LOG_DEBUG(("Creating listener for file transfer"));
-    session->listener = -1;
-    silc_net_check_local_by_sock(conn->sock->sock, NULL, &session->hostname);
-    if (session->hostname)
-      session->listener = silc_net_create_server(0, session->hostname);
-    if (session->listener < 0) {
-      SILC_LOG_DEBUG(("Could not create listener"));
-      session->listener = 0;
-      client->internal->ops->say(client, conn, SILC_CLIENT_MESSAGE_ERROR,
-                                "Cannot create listener on %s: %s",
-                                session->hostname, strerror(errno));
+    if (!params || (!params->local_ip && !params->bind_ip)) {
+      silc_free(session);
       return SILC_CLIENT_FILE_ERROR;
     }
-    session->port = silc_net_get_local_port(session->listener);
-    silc_schedule_task_add(client->schedule, session->listener,
-                          silc_client_ftp_process_key_agreement, session,
-                          0, 0, SILC_TASK_FD, SILC_TASK_PRI_NORMAL);
+    session->listener =
+      silc_client_listener_add(client, conn->internal->schedule, params,
+                              public_key, private_key,
+                              silc_client_ftp_connect_completion,
+                              session);
+    if (!session->listener) {
+      client->internal->ops->say(client, conn, SILC_CLIENT_MESSAGE_ERROR,
+                                "Cannot create listener for file transfer: "
+                                "%s", strerror(errno));
+      silc_free(session);
+      return SILC_CLIENT_FILE_NO_MEMORY;
+    }
+    session->hostname = (params->bind_ip ? strdup(params->bind_ip) :
+                        strdup(params->local_ip));
+    session->port = silc_client_listener_get_local_port(session->listener);
 
     /* Send the key agreement inside FTP packet */
     SILC_LOG_DEBUG(("Sending key agreement for file transfer"));
-    keyagr = silc_key_agreement_payload_encode(session->hostname,
+    keyagr = silc_key_agreement_payload_encode(session->hostname, 0,
                                               session->port);
-    ftp = silc_buffer_alloc(1 + keyagr->len);
-    silc_buffer_pull_tail(ftp, SILC_BUFFER_END(ftp));
-    silc_buffer_format(ftp,
-                      SILC_STR_UI_CHAR(1),
-                      SILC_STR_UI_XNSTRING(keyagr->data, keyagr->len),
-                      SILC_STR_END);
-    silc_client_packet_send(client, conn->sock, SILC_PACKET_FTP,
-                           session->client_entry->id,
-                           SILC_ID_CLIENT, NULL, NULL,
-                           ftp->data, ftp->len, FALSE);
-
+    if (!keyagr) {
+      silc_client_listener_free(session->listener);
+      silc_free(session);
+      return SILC_CLIENT_FILE_NO_MEMORY;
+    }
+    silc_packet_send_va_ext(conn->stream, SILC_PACKET_FTP, 0, 0, NULL,
+                           SILC_ID_CLIENT, &session->client_entry->id,
+                           NULL, NULL,
+                           SILC_STR_UI_CHAR(1),
+                           SILC_STR_DATA(silc_buffer_data(keyagr),
+                                         silc_buffer_len(keyagr)),
+                           SILC_STR_END);
     silc_buffer_free(keyagr);
-    silc_buffer_free(ftp);
+
+    /* Add session request timeout */
+    if (params && params->timeout_secs)
+      silc_schedule_task_add_timeout(client->schedule,
+                                    silc_client_ftp_timeout, session,
+                                    params->timeout_secs, 0);
   }
 
   return SILC_CLIENT_FILE_OK;
 }
 
-SILC_TASK_CALLBACK(silc_client_file_close_final)
-{
-  silc_client_ftp_session_free(context);
-}
-
 /* Closes file transmission session indicated by the `session_id'.
    If file transmission is being conducted it will be aborted
    automatically. This function is also used to close the session
@@ -1065,13 +910,14 @@ SilcClientFileError silc_client_file_close(SilcClient client,
 {
   SilcClientFtpSession session;
 
-  assert(client && conn);
+  if (!client || !conn)
+    return SILC_CLIENT_FILE_ERROR;
 
-  SILC_LOG_DEBUG(("Start, Session ID: %d", session_id));
+  SILC_LOG_DEBUG(("Closing file transer session %d", session_id));
 
   /* Get the session */
-  silc_dlist_start(conn->internal->ftp_sessions);
-  while ((session = silc_dlist_get(conn->internal->ftp_sessions))
+  silc_dlist_start(client->internal->ftp_sessions);
+  while ((session = silc_dlist_get(client->internal->ftp_sessions))
         != SILC_LIST_END) {
     if (session->session_id == session_id)
       break;
@@ -1082,45 +928,29 @@ SilcClientFileError silc_client_file_close(SilcClient client,
     return SILC_CLIENT_FILE_UNKNOWN_SESSION;
   }
 
-  if (session->monitor)
+  if (session->monitor) {
     (*session->monitor)(session->client, session->conn,
                        SILC_CLIENT_FILE_MONITOR_CLOSED,
                        SILC_CLIENT_FILE_OK, 0, 0,
                        session->client_entry, session->session_id,
                        session->filepath, session->monitor_context);
 
+    /* No more callbacks to application */
+    session->monitor = NULL;
+  }
+
+  session->closed = TRUE;
+
   /* Destroy via timeout */
-  silc_schedule_task_add(session->client->schedule, 0,
-                        silc_client_file_close_final, session,
-                        0, 1, SILC_TASK_TIMEOUT, SILC_TASK_PRI_NORMAL);
+  silc_schedule_task_add_timeout(conn->internal->schedule,
+                                silc_client_file_close_final, session,
+                                0, 1);
 
   return SILC_CLIENT_FILE_OK;
 }
 
 /************************** FTP Request Processing **************************/
 
-/* Client resolving callback.  Continues with the FTP processing */
-
-static void silc_client_ftp_client_resolved(SilcClient client,
-                                           SilcClientConnection conn,
-                                           SilcStatus status,
-                                           SilcDList clients,
-                                           void *context)
-{
-  SilcFSMThread thread = context;
-  SilcPacket packet = silc_fsm_get_state_context(thread);
-
-  /* If no client found, ignore the packet, a silent error */
-  if (!clients) {
-    silc_packet_free(packet);
-    silc_fsm_finish(thread);
-    return;
-  }
-
-  /* Continue processing the packet */
-  SILC_FSM_CALL_CONTINUE(context);
-}
-
 /* Received file transfer packet.  Only file transfer requests get here.
    The actual file transfer is handled by the SFTP library when we give it
    the packet stream wrapped into SilcStream context. */
@@ -1130,13 +960,12 @@ SILC_FSM_STATE(silc_client_ftp)
   SilcClientConnection conn = fsm_context;
   SilcClient client = conn->client;
   SilcPacket packet = state_context;
+  SilcClientFtpSession session;
   SilcClientID remote_id;
   SilcClientEntry remote_client;
   SilcKeyAgreementPayload payload;
-  SilcUInt8 type;
   char *hostname;
   SilcUInt16 port;
-  int ret;
 
   SILC_LOG_DEBUG(("Process file transfer packet"));
 
@@ -1158,7 +987,7 @@ SILC_FSM_STATE(silc_client_ftp)
 
   /* Check whether we know this client already */
   remote_client = silc_client_get_client_by_id(client, conn, &remote_id);
-  if (!remote_client || !remote_client->nickname[0]) {
+  if (!remote_client || !remote_client->internal.valid) {
     /** Resolve client info */
     silc_client_unref_client(client, conn, remote_client);
     SILC_FSM_CALL(silc_client_get_client_by_id_resolve(
@@ -1168,18 +997,18 @@ SILC_FSM_STATE(silc_client_ftp)
     /* NOT REACHED */
   }
 
-
-  silc_dlist_start(conn->internal->ftp_sessions);
-  while ((session = silc_dlist_get(conn->internal->ftp_sessions))
-        != SILC_LIST_END) {
-    if (session->client_entry == client_entry &&
-       (!session->server || !session->bound))
+  /* Get session */
+  silc_dlist_start(client->internal->ftp_sessions);
+  while ((session = silc_dlist_get(client->internal->ftp_sessions))) {
+    if (session->client_entry == remote_client &&
+       (!session->initiator || !session->listener))
       break;
   }
 
   /* Parse the key agreement payload */
-  payload = silc_key_agreement_payload_parse(packet->buffer->data + 1,
-                                            packet->buffer->len - 1);
+  payload =
+    silc_key_agreement_payload_parse(silc_buffer_data(&packet->buffer) + 1,
+                                    silc_buffer_len(&packet->buffer) - 1);
   if (!payload) {
     SILC_LOG_DEBUG(("Invalid key agreement payload"));
     goto out;
@@ -1187,52 +1016,54 @@ SILC_FSM_STATE(silc_client_ftp)
 
   hostname = silc_key_agreement_get_hostname(payload);
   port = silc_key_agreement_get_port(payload);
-  if (!hostname)
-    port = 0;
-  if (!port)
+  if (!hostname || !port) {
     hostname = NULL;
+    port = 0;
+  }
+
+  /* If session doesn't exist, we create new one.  If session exists, but
+     we are responder it means that the remote sent another request and user
+     hasn't even accepted the first one yet.  We assume this session is new
+     session as well. */
+  if (!session || !hostname || !session->initiator) {
+    /* New file transfer session */
+    SILC_LOG_DEBUG(("New file transfer session %d",
+                   client->internal->next_session_id + 1));
 
-  /* If session doesn't exist, we create one and let applicationi know about
-     incoming file transfer request.  If session exists, but we are responder
-     it means that the remote sent another request and user hasn't even
-     accepted the first one yet.  We assume this session is new session
-     as well. */
-  if (session == SILC_LIST_END || (!hostname && !port) ||
-      (session && session->server == FALSE)) {
-    /* No session found, create one and let the application know about
-       incoming file transfer request. */
-    SILC_LOG_DEBUG(("New file transfer session ID: %d",
-                   conn->internal->next_session_id + 1));
-
-    /* Add new session */
     session = silc_calloc(1, sizeof(*session));
-    session->session_id = ++conn->internal->next_session_id;
+    if (!session)
+      goto out;
+    session->session_id = ++client->internal->next_session_id;
     session->client = client;
-    session->conn = conn;
-    session->client_entry = client_entry;
-    silc_dlist_add(conn->internal->ftp_sessions, session);
-
+    session->client_entry = silc_client_ref_client(client, conn,
+                                                  remote_client);
     if (hostname && port) {
       session->hostname = strdup(hostname);
       session->port = port;
     }
+    silc_dlist_add(client->internal->ftp_sessions, session);
 
-    /* Let the application know */
-    client->internal->ops->ftp(client, conn, client_entry,
+    /* Notify application of incoming FTP request */
+    client->internal->ops->ftp(client, conn, remote_client,
                               session->session_id, hostname, port);
-
     goto out;
   }
 
   /* Session exists, continue with key agreement protocol. */
-  SILC_LOG_DEBUG(("Session ID %d exists, connecting to remote client",
+  SILC_LOG_DEBUG(("Session %d exists, connecting to remote client",
                  session->session_id));
 
   session->hostname = strdup(hostname);
   session->port = port;
 
-  if (silc_client_connect_to_client(client, conn, port,
-                                   hostname, session) < 0) {
+  /* Connect to the remote client.  Performs key exchange automatically. */
+  session->op =
+    silc_client_connect_to_client(client, &session->params,
+                                 session->public_key, session->private_key,
+                                 session->hostname, session->port,
+                                 silc_client_ftp_connect_completion,
+                                 session);
+  if (!session->op) {
     /* Call monitor callback */
     if (session->monitor)
       (*session->monitor)(session->client, session->conn,
@@ -1242,8 +1073,6 @@ SILC_FSM_STATE(silc_client_ftp)
                          session->filepath, session->monitor_context);
   }
 
-
-
  out:
   silc_packet_free(packet);
   return SILC_FSM_FINISH;
diff --git a/lib/silcclient/client_ftp.h b/lib/silcclient/client_ftp.h
new file mode 100644 (file)
index 0000000..13e415e
--- /dev/null
@@ -0,0 +1,28 @@
+/*
+
+  client_ftp.h
+
+  Author: Pekka Riikonen <priikone@silcnet.org>
+
+  Copyright (C) 2007 Pekka Riikonen
+
+  This program is free software; you can redistribute it and/or modify
+  it under the terms of the GNU General Public License as published by
+  the Free Software Foundation; version 2 of the License.
+
+  This program is distributed in the hope that it will be useful,
+  but WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+  GNU General Public License for more details.
+
+*/
+
+#ifndef CLIENT_FTP_H
+#define CLIENT_FTP_H
+
+SILC_FSM_STATE(silc_client_ftp);
+void silc_client_ftp_free_sessions(SilcClient client);
+void silc_client_ftp_session_free_client(SilcClient client,
+                                        SilcClientEntry client_entry);
+
+#endif /* CLIENT_FTP_H */
index 17d1fad0ede54b7451c2c028f536cf66eb01aa2c..dd331196ac150732d31d481f0502b75062c7b0cd 100644 (file)
@@ -29,6 +29,8 @@
 #include "client_channel.h"
 #include "client_notify.h"
 #include "client_keyagr.h"
+#include "client_ftp.h"
+#include "client_listener.h"
 
 /****************************** Definitions *********************************/
 
@@ -173,5 +175,15 @@ void silc_client_del_connection(SilcClient client, SilcClientConnection conn);
 void silc_client_fsm_destructor(SilcFSM fsm, void *fsm_context,
                                void *destructor_context);
 void silc_client_command_free(SilcClientCommandContext cmd);
+SilcClientConnection
+silc_client_add_connection(SilcClient client,
+                          SilcConnectionType conn_type,
+                          SilcBool connect,
+                          SilcClientConnectionParams *params,
+                          SilcPublicKey public_key,
+                          SilcPrivateKey private_key,
+                          char *remote_host, int port,
+                          SilcClientConnectCallback callback,
+                          void *context);
 
 #endif /* CLIENT_INTERNAL_H */
index 15d0f13048e55ea484e9f81f0e30b7db5e9020ee..221bd8a30392bdc8716e4dea0d306f39dbeef02e 100644 (file)
 struct SilcClientKeyAgreementStruct {
   SilcClient client;                     /* Client */
   SilcClientConnection conn;             /* Server connection */
+  SilcClientListener listener;           /* Listener */
   SilcKeyAgreementCallback completion;   /* Key agreement completion */
   void *context;                         /* User context */
-  SilcClientConnectionParams params;      /* Connection parameters */
-  SilcPublicKey public_key;              /* Responder public key */
-  SilcPrivateKey private_key;            /* Responder private key */
-  SilcNetListener tcp_listener;                  /* TCP listener */
-  SilcPacketStream udp_listener;         /* UDP listener */
-  SilcPacketStream stream;               /* Remote connection (TCP or UDP) */
-  SilcAsyncOperation op;                 /* SKE operation */
-  SilcSKE ske;                           /* SKE */
+  SilcAsyncOperation op;                 /* Async operation, initiator */
 };
 
 /************************ Static utility functions **************************/
@@ -49,21 +43,13 @@ static void silc_client_keyagr_free(SilcClient client,
 {
   SilcClientKeyAgreement ke = client_entry->internal.ke;
 
+  silc_client_listener_free(ke->listener);
   silc_schedule_task_del_by_context(conn->internal->schedule, client_entry);
-
   if (ke->op)
     silc_async_abort(ke->op, NULL, NULL);
-  if (ke->ske)
-    silc_ske_free(ke->ske);
-  if (ke->tcp_listener)
-    silc_net_close_listener(ke->tcp_listener);
-  silc_packet_stream_destroy(ke->stream);
-  silc_packet_stream_destroy(ke->udp_listener);
-
   client_entry->internal.ke = NULL;
   client_entry->internal.prv_resp = FALSE;
   silc_client_unref_client(client, conn, client_entry);
-
   silc_free(ke);
 }
 
@@ -98,198 +84,12 @@ static void silc_client_keyagr_resolved(SilcClient client,
   SILC_FSM_CALL_CONTINUE(context);
 }
 
-/* Called after application has verified remote host's public key.  Responder
-   function. */
-
-static void silc_client_keyagr_verify_key_cb(SilcBool success, void *context)
-{
-  SilcVerifyKeyContext verify = context;
-
-  /* Call the completion callback back to the SKE */
-  verify->completion(verify->ske, success ? SILC_SKE_STATUS_OK :
-                    SILC_SKE_STATUS_UNSUPPORTED_PUBLIC_KEY,
-                    verify->completion_context);
-
-  silc_free(verify);
-}
-
-/* Verify remote host's public key.  Responder function. */
-
-static void silc_client_keyagr_verify_key(SilcSKE ske,
-                                         SilcPublicKey public_key,
-                                         void *context,
-                                         SilcSKEVerifyCbCompletion completion,
-                                         void *completion_context)
-{
-  SilcClientEntry client_entry = context;
-  SilcClientKeyAgreement ke = client_entry->internal.ke;
-  SilcClientConnection conn = ke->conn;
-  SilcClient client = conn->client;
-  SilcVerifyKeyContext verify;
-
-  /* If we provided repository for SKE and we got here the key was not
-     found from the repository. */
-  if (ke->params.repository && !ke->params.verify_notfound) {
-    completion(ske, SILC_SKE_STATUS_UNSUPPORTED_PUBLIC_KEY,
-              completion_context);
-    return;
-  }
-
-  SILC_LOG_DEBUG(("Verify remote public key"));
-
-  verify = silc_calloc(1, sizeof(*verify));
-  if (!verify) {
-    completion(ske, SILC_SKE_STATUS_UNSUPPORTED_PUBLIC_KEY,
-              completion_context);
-    return;
-  }
-  verify->ske = ske;
-  verify->completion = completion;
-  verify->completion_context = completion_context;
-
-  /* Verify public key in application */
-  client->internal->ops->verify_public_key(client, conn,
-                                          SILC_CONN_CLIENT, public_key,
-                                          silc_client_keyagr_verify_key_cb,
-                                          verify);
-}
-
-/* Key exchange protocol completion callback.  Responder function. */
-
-static void silc_client_keyagr_completion(SilcSKE ske,
-                                         SilcSKEStatus status,
-                                         SilcSKESecurityProperties prop,
-                                         SilcSKEKeyMaterial keymat,
-                                         SilcSKERekeyMaterial rekey,
-                                         void *context)
-{
-  SilcClientEntry client_entry = context;
-  SilcClientKeyAgreement ke = client_entry->internal.ke;
-  SilcClientConnection conn = ke->conn;
-  SilcClient client = conn->client;
-
-  if (status != SILC_SKE_STATUS_OK) {
-    /* Key exchange failed */
-    ke->completion(client, conn, client_entry,
-                  status == SILC_SKE_STATUS_TIMEOUT ?
-                  SILC_KEY_AGREEMENT_TIMEOUT :
-                  SILC_KEY_AGREEMENT_FAILURE, NULL, ke->context);
-    silc_client_keyagr_free(client, conn, client_entry);
-    return;
-  }
-
-  /* Returns the negotiated key material to application.  Key agreement
-     was successful. */
-  ke->completion(client, conn, client_entry, SILC_KEY_AGREEMENT_OK,
-                keymat, ke->context);
-
-  silc_client_keyagr_free(client, conn, client_entry);
-}
-
-/* Starts key agreement as responder. */
-
-static void silc_client_process_key_agreement(SilcClient client,
-                                             SilcClientConnection conn,
-                                             SilcClientEntry client_entry)
-{
-  SilcClientKeyAgreement ke = client_entry->internal.ke;
-  SilcSKEParamsStruct params;
-
-  SILC_LOG_DEBUG(("Processing key agrement %p session", ke));
-
-  /* Allocate SKE */
-  ke->ske = silc_ske_alloc(client->rng, conn->internal->schedule,
-                          ke->params.repository, ke->public_key,
-                          ke->private_key, client_entry);
-  if (!ke->ske) {
-    ke->completion(client, conn, client_entry, SILC_KEY_AGREEMENT_NO_MEMORY,
-                  NULL, ke->context);
-    silc_client_keyagr_free(client, conn, client_entry);
-    return;
-  }
-
-  /* Set SKE parameters */
-  params.version = client->internal->silc_client_version;
-  params.flags = SILC_SKE_SP_FLAG_MUTUAL;
-  if (ke->params.udp) {
-    params.flags |= SILC_SKE_SP_FLAG_IV_INCLUDED;
-    params.session_port = ke->params.local_port;
-  }
-
-  silc_ske_set_callbacks(ke->ske, silc_client_keyagr_verify_key,
-                        silc_client_keyagr_completion, client_entry);
-
-  /* Start key exchange as responder */
-  ke->op = silc_ske_responder(ke->ske, ke->stream, &params);
-  if (!ke->op) {
-    ke->completion(client, conn, client_entry, SILC_KEY_AGREEMENT_ERROR,
-                  NULL, ke->context);
-    silc_client_keyagr_free(client, conn, client_entry);
-  }
-}
-
-/* TCP network listener callback.  Accepts new key agreement connection.
-   Responder function. */
-
-static void silc_client_tcp_accept(SilcNetStatus status,
-                                  SilcStream stream,
-                                  void *context)
-{
-  SilcClientEntry client_entry = context;
-  SilcClientKeyAgreement ke = client_entry->internal.ke;
-
-  /* Create packet stream */
-  ke->stream = silc_packet_stream_create(ke->client->internal->packet_engine,
-                                        ke->conn->internal->schedule, stream);
-  if (!ke->stream) {
-    silc_stream_destroy(stream);
-    return;
-  }
-
-  /* Process session */
-  silc_client_process_key_agreement(ke->client, ke->conn, client_entry);
-}
-
-/* UDP network listener callback.  Accepts new key agreement session.
-   Responder function. */
+/* Key exchange completion callback.  Called after connected to remote host
+   and performed key exchange, when we are initiator.  As responder, this is
+   called after the remote has connected to us and have performed the key
+   exchange. */
 
-static SilcBool silc_client_udp_accept(SilcPacketEngine engine,
-                                       SilcPacketStream stream,
-                                       SilcPacket packet,
-                                       void *callback_context,
-                                       void *stream_context)
-{
-  SilcClientEntry client_entry = callback_context;
-  SilcClientKeyAgreement ke = client_entry->internal.ke;
-  SilcUInt16 port;
-  const char *ip;
-
-  /* We want only key exchange packet.  Eat other packets so that default
-     packet callback doesn't get them. */
-  if (packet->type != SILC_PACKET_KEY_EXCHANGE) {
-    silc_packet_free(packet);
-    return TRUE;
-  }
-
-  /* Create packet stream for this remote UDP session */
-  if (!silc_packet_get_sender(packet, &ip, &port)) {
-    silc_packet_free(packet);
-    return TRUE;
-  }
-  ke->stream = silc_packet_stream_add_remote(stream, ip, port, packet);
-  if (!ke->stream) {
-    silc_packet_free(packet);
-    return TRUE;
-  }
-
-  /* Process session */
-  silc_client_process_key_agreement(ke->client, ke->conn, client_entry);
-  return TRUE;
-}
-
-/* Client connect completion callback.  Initiator function. */
-
-static void silc_client_keyagr_perform_cb(SilcClient client,
+static void silc_client_keyagr_completion(SilcClient client,
                                          SilcClientConnection conn,
                                          SilcClientConnectionStatus status,
                                          SilcStatus error,
@@ -324,19 +124,13 @@ static void silc_client_keyagr_perform_cb(SilcClient client,
     break;
   }
 
-  /* Close the created connection */
+  /* Close the connection */
   if (conn)
     silc_client_close_connection(ke->client, conn);
 
   silc_client_keyagr_free(ke->client, ke->conn, client_entry);
 }
 
-/* Packet stream callbacks */
-static SilcPacketCallbacks silc_client_keyagr_stream_cb =
-{
-  silc_client_udp_accept, NULL, NULL
-};
-
 /*************************** Key Agreement API ******************************/
 
 /* Sends key agreement packet to remote client.  If IP addresses are provided
@@ -356,7 +150,6 @@ void silc_client_send_key_agreement(SilcClient client,
   SilcBuffer buffer;
   SilcUInt16 port = 0, protocol = 0;
   char *local_ip = NULL;
-  SilcStream stream;
 
   SILC_LOG_DEBUG(("Sending key agreement"));
 
@@ -377,7 +170,8 @@ void silc_client_send_key_agreement(SilcClient client,
     return;
   }
 
-  /* If local IP is provided, create listener */
+  /* If local IP is provided, create listener.  If this is not provided,
+     we'll just send empty key agreement payload */
   if (params && (params->local_ip || params->bind_ip)) {
     ke = silc_calloc(1, sizeof(*ke));
     if (!ke) {
@@ -386,70 +180,15 @@ void silc_client_send_key_agreement(SilcClient client,
       return;
     }
 
-    /* Create network listener */
-    if (params->udp) {
-      /* UDP listener */
-      stream = silc_net_udp_connect(params->bind_ip ? params->bind_ip :
-                                   params->local_ip, params->local_port,
-                                   NULL, 0, conn->internal->schedule);
-      ke->udp_listener =
-       silc_packet_stream_create(client->internal->packet_engine,
-                                 conn->internal->schedule, stream);
-      if (!ke->udp_listener) {
-       client->internal->ops->say(
-                    client, conn, SILC_CLIENT_MESSAGE_ERROR,
-                    "Cannot create UDP listener on %s on port %d: %s",
-                    params->bind_ip ? params->bind_ip :
-                    params->local_ip, params->local_port, strerror(errno));
-       completion(client, conn, client_entry, SILC_KEY_AGREEMENT_ERROR,
-                  NULL, context);
-       if (stream)
-         silc_stream_destroy(stream);
-       silc_free(ke);
-       return;
-      }
-      silc_packet_stream_link(ke->udp_listener,
-                             &silc_client_keyagr_stream_cb,
-                             client_entry, 1000000,
-                             SILC_PACKET_ANY, -1);
-
-      port = params->local_port;
-      if (!port) {
-       /* Get listener port */
-       SilcSocket sock;
-       silc_socket_stream_get_info(stream, &sock, NULL, NULL, NULL);
-       port = silc_net_get_local_port(sock);
-      }
-    } else {
-      /* TCP listener */
-      ke->tcp_listener =
-       silc_net_tcp_create_listener(params->bind_ip ?
-                                    (const char **)&params->bind_ip :
-                                    (const char **)&params->local_ip,
-                                    1, params->local_port, FALSE, FALSE,
-                                    conn->internal->schedule,
-                                    silc_client_tcp_accept,
-                                    client_entry);
-      if (!ke->tcp_listener) {
-       client->internal->ops->say(
-                    client, conn, SILC_CLIENT_MESSAGE_ERROR,
-                    "Cannot create listener on %s on port %d: %s",
-                    params->bind_ip ? params->bind_ip :
-                    params->local_ip, params->local_port, strerror(errno));
-       completion(client, conn, client_entry, SILC_KEY_AGREEMENT_ERROR,
-                  NULL, context);
-       silc_free(ke);
-       return;
-      }
-
-      port = params->local_port;
-      if (!port) {
-       /* Get listener port */
-       SilcUInt16 *ports;
-       ports = silc_net_listener_get_port(ke->tcp_listener, NULL);
-       port = ports[0];
-       silc_free(ports);
-      }
+    /* Create listener */
+    ke->listener = silc_client_listener_add(client, conn->internal->schedule,
+                                           params, public_key, private_key,
+                                           silc_client_keyagr_completion,
+                                           client_entry);
+    if (!ke->listener) {
+      completion(client, conn, client_entry, SILC_KEY_AGREEMENT_NO_MEMORY,
+                NULL, context);
+      return;
     }
 
     local_ip = params->local_ip;
@@ -459,9 +198,6 @@ void silc_client_send_key_agreement(SilcClient client,
     ke->conn = conn;
     ke->completion = completion;
     ke->context = context;
-    ke->params = *params;
-    ke->public_key = public_key;
-    ke->private_key = private_key;
     silc_client_ref_client(client, conn, client_entry);
     client_entry->internal.ke = ke;
     client_entry->internal.prv_resp = TRUE;
@@ -541,10 +277,11 @@ void silc_client_perform_key_agreement(SilcClient client,
     params->no_authentication = TRUE;
 
   /* Connect to the remote client.  Performs key exchange automatically. */
-  if (!silc_client_connect_to_client(client, params, public_key,
-                                    private_key, hostname, port,
-                                    silc_client_keyagr_perform_cb,
-                                    client_entry)) {
+  ke->op = silc_client_connect_to_client(client, params, public_key,
+                                        private_key, hostname, port,
+                                        silc_client_keyagr_completion,
+                                        client_entry);
+  if (!ke->op) {
     completion(client, conn, client_entry, SILC_KEY_AGREEMENT_ERROR,
               NULL, context);
     silc_client_keyagr_free(client, conn, client_entry);
@@ -598,10 +335,11 @@ silc_client_perform_key_agreement_stream(SilcClient client,
     params->no_authentication = TRUE;
 
   /* Perform key exchange protocol */
-  if (!silc_client_key_exchange(client, params, public_key,
-                               private_key, stream, SILC_CONN_CLIENT,
-                               silc_client_keyagr_perform_cb,
-                               client_entry)) {
+  ke->op = silc_client_key_exchange(client, params, public_key,
+                                   private_key, stream, SILC_CONN_CLIENT,
+                                   silc_client_keyagr_completion,
+                                   client_entry);
+  if (!ke->op) {
     completion(client, conn, client_entry, SILC_KEY_AGREEMENT_ERROR,
               NULL, context);
     silc_client_keyagr_free(client, conn, client_entry);
diff --git a/lib/silcclient/client_listener.c b/lib/silcclient/client_listener.c
new file mode 100644 (file)
index 0000000..d6af269
--- /dev/null
@@ -0,0 +1,404 @@
+/*
+
+  client_listener.c
+
+  Author: Pekka Riikonen <priikone@silcnet.org>
+
+  Copyright (C) 2007 Pekka Riikonen
+
+  This program is free software; you can redistribute it and/or modify
+  it under the terms of the GNU General Public License as published by
+  the Free Software Foundation; version 2 of the License.
+
+  This program is distributed in the hope that it will be useful,
+  but WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+  GNU General Public License for more details.
+
+*/
+
+#include "silc.h"
+#include "silcclient.h"
+#include "client_internal.h"
+
+/************************** Types and definitions ***************************/
+
+/* Listener context */
+struct SilcClientListenerStruct {
+  SilcClient client;                     /* Client */
+  SilcSchedule schedule;                 /* Scheduler */
+  SilcClientConnectCallback callback;    /* Connection callback */
+  void *context;                         /* User context */
+  SilcClientConnectionParams params;      /* Connection parameters */
+  SilcPublicKey public_key;              /* Responder public key */
+  SilcPrivateKey private_key;            /* Responder private key */
+  SilcNetListener tcp_listener;                  /* TCP listener */
+  SilcPacketStream udp_listener;         /* UDP listener */
+};
+
+/************************ Static utility functions **************************/
+
+/* Called after application has verified remote host's public key. */
+
+static void silc_client_listener_verify_key_cb(SilcBool success, void *context)
+{
+  SilcVerifyKeyContext verify = context;
+
+  /* Call the completion callback back to the SKE */
+  verify->completion(verify->ske, success ? SILC_SKE_STATUS_OK :
+                    SILC_SKE_STATUS_UNSUPPORTED_PUBLIC_KEY,
+                    verify->completion_context);
+
+  silc_free(verify);
+}
+
+/* Verify remote host's public key. */
+
+static void
+silc_client_listener_verify_key(SilcSKE ske,
+                               SilcPublicKey public_key,
+                               void *context,
+                               SilcSKEVerifyCbCompletion completion,
+                               void *completion_context)
+{
+  SilcClientConnection conn = context;
+  SilcClient client = conn->client;
+  SilcVerifyKeyContext verify;
+
+  /* If we provided repository for SKE and we got here the key was not
+     found from the repository. */
+  if (conn->internal->params.repository &&
+      !conn->internal->params.verify_notfound) {
+    completion(ske, SILC_SKE_STATUS_UNSUPPORTED_PUBLIC_KEY,
+              completion_context);
+    return;
+  }
+
+  SILC_LOG_DEBUG(("Verify remote public key"));
+
+  verify = silc_calloc(1, sizeof(*verify));
+  if (!verify) {
+    completion(ske, SILC_SKE_STATUS_UNSUPPORTED_PUBLIC_KEY,
+              completion_context);
+    return;
+  }
+  verify->ske = ske;
+  verify->completion = completion;
+  verify->completion_context = completion_context;
+
+  /* Verify public key in application */
+  client->internal->ops->verify_public_key(client, conn,
+                                          SILC_CONN_CLIENT, public_key,
+                                          silc_client_listener_verify_key_cb,
+                                          verify);
+}
+
+/* Key exchange protocol completion callback. */
+
+static void silc_client_listener_completion(SilcSKE ske,
+                                           SilcSKEStatus status,
+                                           SilcSKESecurityProperties prop,
+                                           SilcSKEKeyMaterial keymat,
+                                           SilcSKERekeyMaterial rekey,
+                                           void *context)
+{
+  SilcClientConnection conn = context;
+  SilcCipher send_key, receive_key;
+  SilcHmac hmac_send, hmac_receive;
+
+  SILC_LOG_DEBUG(("Key exchange completed"));
+
+  if (status != SILC_SKE_STATUS_OK) {
+    /* Key exchange failed */
+    conn->callback(conn->client, conn,
+                  status == SILC_SKE_STATUS_TIMEOUT ?
+                  SILC_CLIENT_CONN_ERROR_TIMEOUT :
+                  SILC_CLIENT_CONN_ERROR_KE, conn->internal->error,
+                  conn->internal->disconnect_message,
+                  conn->callback_context);
+    return;
+  }
+
+  /* Allocate the cipher and HMAC contexts */
+  if (!silc_ske_set_keys(ske, keymat, prop, &send_key, &receive_key,
+                        &hmac_send, &hmac_receive, &conn->internal->hash)) {
+    conn->callback(conn->client, conn,
+                  SILC_CLIENT_CONN_ERROR_KE, 0, NULL,
+                  conn->callback_context);
+    return;
+  }
+
+  /* Set the keys into the packet stream.  After this call packets will be
+     encrypted with these keys. */
+  if (!silc_packet_set_keys(conn->stream, send_key, receive_key, hmac_send,
+                           hmac_receive, FALSE)) {
+    conn->callback(conn->client, conn,
+                  SILC_CLIENT_CONN_ERROR_KE, 0, NULL,
+                  conn->callback_context);
+    return;
+  }
+
+  /* Key exchange successful */
+  conn->callback(conn->client, conn, SILC_CLIENT_CONN_SUCCESS, 0, NULL,
+                conn->callback_context);
+}
+
+/* Starts key agreement as responder. */
+
+static void
+silc_client_listener_new_connection(SilcClientListener listener,
+                                   SilcPacketStream stream)
+{
+  SilcClient client = listener->client;
+  SilcClientConnection conn;
+  SilcSKEParamsStruct params;
+  const char *hostname = NULL, *ip = NULL;
+  SilcUInt16 port;
+
+  /* Get remote information */
+  silc_socket_stream_get_info(silc_packet_stream_get_stream(stream),
+                             NULL, &hostname, &ip, &port);
+  if (!ip || !port) {
+    listener->callback(client, NULL, SILC_CLIENT_CONN_ERROR, 0, NULL,
+                      listener->context);
+    silc_packet_stream_destroy(stream);
+    return;
+  }
+  if (!hostname)
+    hostname = ip;
+
+  /* Add new connection */
+  conn = silc_client_add_connection(client, SILC_CONN_CLIENT, FALSE,
+                                   &listener->params,
+                                   listener->public_key,
+                                   listener->private_key,
+                                   (char *)hostname, port,
+                                   listener->callback, listener->context);
+  if (!conn) {
+    listener->callback(client, NULL, SILC_CLIENT_CONN_ERROR, 0, NULL,
+                      listener->context);
+    silc_packet_stream_destroy(stream);
+    return;
+  }
+  conn->stream = stream;
+  conn->internal->schedule = listener->schedule;
+  silc_packet_set_context(conn->stream, conn);
+
+  SILC_LOG_DEBUG(("Processing new incoming connection %p", conn));
+
+  /* Allocate SKE */
+  conn->internal->ske =
+    silc_ske_alloc(client->rng, conn->internal->schedule,
+                  listener->params.repository, listener->public_key,
+                  listener->private_key, listener);
+  if (!conn->internal->ske) {
+    conn->callback(conn->client, conn, SILC_CLIENT_CONN_ERROR, 0, NULL,
+                  conn->callback_context);
+    return;
+  }
+
+  /* Set SKE parameters */
+  params.version = client->internal->silc_client_version;
+  params.flags = SILC_SKE_SP_FLAG_MUTUAL;
+  if (listener->params.udp) {
+    params.flags |= SILC_SKE_SP_FLAG_IV_INCLUDED;
+    params.session_port = listener->params.local_port;
+  }
+
+  silc_ske_set_callbacks(conn->internal->ske, silc_client_listener_verify_key,
+                        silc_client_listener_completion, conn);
+
+  /* Start key exchange as responder */
+  conn->internal->op = silc_ske_responder(conn->internal->ske,
+                                         conn->stream, &params);
+  if (!conn->internal->op)
+    conn->callback(conn->client, conn, SILC_CLIENT_CONN_ERROR, 0, NULL,
+                  conn->callback_context);
+}
+
+/* TCP network listener callback.  Accepts new key agreement connection.
+   Responder function. */
+
+static void silc_client_listener_tcp_accept(SilcNetStatus status,
+                                           SilcStream stream,
+                                           void *context)
+{
+  SilcClientListener listener = context;
+  SilcPacketStream packet_stream;
+
+  SILC_LOG_DEBUG(("New incoming TCP connection"));
+
+  /* Create packet stream */
+  packet_stream =
+    silc_packet_stream_create(listener->client->internal->packet_engine,
+                             listener->schedule, stream);
+  if (!packet_stream) {
+    silc_stream_destroy(stream);
+    return;
+  }
+
+  /* Process session */
+  silc_client_listener_new_connection(listener, packet_stream);
+}
+
+/* UDP network listener callback.  Accepts new key agreement session.
+   Responder function. */
+
+static SilcBool silc_client_udp_accept(SilcPacketEngine engine,
+                                       SilcPacketStream stream,
+                                       SilcPacket packet,
+                                       void *callback_context,
+                                       void *stream_context)
+{
+  SilcClientListener listener = callback_context;
+  SilcPacketStream packet_stream;
+  SilcUInt16 port;
+  const char *ip;
+
+  SILC_LOG_DEBUG(("New incoming UDP connection"));
+
+  /* We want only key exchange packet.  Eat other packets so that default
+     packet callback doesn't get them. */
+  if (packet->type != SILC_PACKET_KEY_EXCHANGE) {
+    silc_packet_free(packet);
+    return TRUE;
+  }
+
+  /* Create packet stream for this remote UDP session */
+  if (!silc_packet_get_sender(packet, &ip, &port)) {
+    silc_packet_free(packet);
+    return TRUE;
+  }
+  packet_stream = silc_packet_stream_add_remote(stream, ip, port, packet);
+  if (!packet_stream) {
+    silc_packet_free(packet);
+    return TRUE;
+  }
+
+  /* Process session */
+  silc_client_listener_new_connection(listener, packet_stream);
+  return TRUE;
+}
+
+/* Packet stream callbacks */
+static SilcPacketCallbacks silc_client_listener_stream_cb =
+{
+  silc_client_udp_accept, NULL, NULL
+};
+
+/***************************** Listner routines *****************************/
+
+/* Adds network listener.  The `callback' will be called after new conection
+   has arrived and key exchange protocol has been completed. */
+
+SilcClientListener
+silc_client_listener_add(SilcClient client,
+                        SilcSchedule schedule,
+                        SilcClientConnectionParams *params,
+                        SilcPublicKey public_key,
+                        SilcPrivateKey private_key,
+                        SilcClientConnectCallback callback,
+                        void *context)
+{
+  SilcClientListener listener;
+  SilcStream stream;
+
+  if (!client || !schedule ||
+      !params || (!params->local_ip && !params->bind_ip))
+    return NULL;
+
+  SILC_LOG_DEBUG(("Adding new listener"));
+
+  listener = silc_calloc(1, sizeof(*listener));
+  if (!listener)
+    return NULL;
+  listener->client = client;
+  listener->schedule = schedule;
+  listener->callback = callback;
+  listener->context = context;
+  listener->params = *params;
+  listener->public_key = public_key;
+  listener->private_key = private_key;
+
+  /* Create network listener */
+  if (params->udp) {
+    /* UDP listener */
+    stream = silc_net_udp_connect(params->bind_ip ? params->bind_ip :
+                                 params->local_ip, params->local_port,
+                                 NULL, 0, schedule);
+    listener->udp_listener =
+      silc_packet_stream_create(client->internal->packet_engine,
+                               schedule, stream);
+    if (!listener->udp_listener) {
+      client->internal->ops->say(
+                    client, NULL, SILC_CLIENT_MESSAGE_ERROR,
+                    "Cannot create UDP listener on %s on port %d: %s",
+                    params->bind_ip ? params->bind_ip :
+                    params->local_ip, params->local_port, strerror(errno));
+      silc_client_listener_free(listener);
+      if (stream)
+       silc_stream_destroy(stream);
+      return NULL;
+    }
+    silc_packet_stream_link(listener->udp_listener,
+                           &silc_client_listener_stream_cb, listener,
+                           1000000, SILC_PACKET_ANY, -1);
+
+    if (!params->local_port) {
+      /* Get listener port */
+      SilcSocket sock;
+      silc_socket_stream_get_info(stream, &sock, NULL, NULL, NULL);
+      listener->params.local_port = silc_net_get_local_port(sock);
+    }
+  } else {
+    /* TCP listener */
+    listener->tcp_listener =
+      silc_net_tcp_create_listener(params->bind_ip ?
+                                  (const char **)&params->bind_ip :
+                                  (const char **)&params->local_ip,
+                                  1, params->local_port, TRUE, FALSE,
+                                  schedule, silc_client_listener_tcp_accept,
+                                  listener);
+    if (!listener->tcp_listener) {
+      client->internal->ops->say(
+                    client, NULL, SILC_CLIENT_MESSAGE_ERROR,
+                    "Cannot create listener on %s on port %d: %s",
+                    params->bind_ip ? params->bind_ip :
+                    params->local_ip, params->local_port, strerror(errno));
+
+      silc_client_listener_free(listener);
+      return NULL;
+    }
+
+    if (!params->local_port) {
+      /* Get listener port */
+      SilcUInt16 *ports;
+      ports = silc_net_listener_get_port(listener->tcp_listener, NULL);
+      listener->params.local_port = ports[0];
+      silc_free(ports);
+    }
+  }
+
+  SILC_LOG_DEBUG(("Bound listener to %s:%d",
+                 params->bind_ip ? params->bind_ip : params->local_ip,
+                 listener->params.local_port));
+
+  return listener;
+}
+
+/* Close and free listner */
+
+void silc_client_listener_free(SilcClientListener listener)
+{
+  if (listener->tcp_listener)
+    silc_net_close_listener(listener->tcp_listener);
+  silc_packet_stream_destroy(listener->udp_listener);
+  silc_free(listener);
+}
+
+/* Return listner bound port */
+
+SilcUInt16 silc_client_listener_get_local_port(SilcClientListener listener)
+{
+  return listener->params.local_port;
+}
diff --git a/lib/silcclient/client_listener.h b/lib/silcclient/client_listener.h
new file mode 100644 (file)
index 0000000..0298800
--- /dev/null
@@ -0,0 +1,37 @@
+/*
+
+  client_listener.h
+
+  Author: Pekka Riikonen <priikone@silcnet.org>
+
+  Copyright (C) 2007 Pekka Riikonen
+
+  This program is free software; you can redistribute it and/or modify
+  it under the terms of the GNU General Public License as published by
+  the Free Software Foundation; version 2 of the License.
+
+  This program is distributed in the hope that it will be useful,
+  but WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+  GNU General Public License for more details.
+
+*/
+
+#ifndef CLIENT_LISTENER_H
+#define CLIENT_LISTENER_H
+
+/* Forward declarations */
+typedef struct SilcClientListenerStruct *SilcClientListener;
+
+SilcClientListener
+silc_client_listener_add(SilcClient client,
+                        SilcSchedule schedule,
+                        SilcClientConnectionParams *params,
+                        SilcPublicKey public_key,
+                        SilcPrivateKey private_key,
+                        SilcClientConnectCallback callback,
+                        void *context);
+void silc_client_listener_free(SilcClientListener listener);
+SilcUInt16 silc_client_listener_get_local_port(SilcClientListener listener);
+
+#endif /* CLIENT_LISTENER_H */
index 40a66d3ee9006afe9f7cfaffa20cb76c9df09451..f9a8b131769b90cdb86420283a5e95cd0ac31ef7 100644 (file)
@@ -1410,6 +1410,7 @@ SILC_FSM_STATE(silc_client_notify_server_signoff)
   SilcNotifyType type = silc_notify_get_type(payload);
   SilcArgumentPayload args = silc_notify_get_args(payload);
   SilcClientEntry client_entry;
+  SilcServerEntry server_entry = NULL;
   SilcDList clients;
   SilcID id;
   int i;
@@ -1420,6 +1421,13 @@ SILC_FSM_STATE(silc_client_notify_server_signoff)
   if (!clients)
     goto out;
 
+  /* Get server ID */
+  if (!silc_argument_get_decoded(args, 1, SILC_ARGUMENT_ID, &id, NULL))
+    goto out;
+
+  /* Get server, in case we have it cached */
+  server_entry = silc_client_get_server_by_id(client, conn, &id.u.server_id);
+
   for (i = 1; i < silc_argument_get_arg_num(args); i++) {
     /* Get Client ID */
     if (!silc_argument_get_decoded(args, i + 1, SILC_ARGUMENT_ID, &id, NULL))
@@ -1431,9 +1439,8 @@ SILC_FSM_STATE(silc_client_notify_server_signoff)
       silc_dlist_add(clients, client_entry);
   }
 
-  /* Notify application.  We don't keep server entries so the server
-     entry is returned as NULL. The client's are returned as list. */
-  NOTIFY(client, conn, type, NULL, clients);
+  /* Notify application. */
+  NOTIFY(client, conn, type, server_entry, clients);
 
   /* Delete the clients */
   silc_dlist_start(clients);
@@ -1445,6 +1452,7 @@ SILC_FSM_STATE(silc_client_notify_server_signoff)
 
  out:
   /** Notify processed */
+  silc_client_unref_server(client, conn, server_entry);
   silc_client_list_free(client, conn, clients);
   silc_fsm_next(fsm, silc_client_notify_processed);
   return SILC_FSM_CONTINUE;
index 526c86e34c59ca79ec6d8dc0fc2a5114371664f2..6b6a5b889bfe2f8f8e24b7e4f4e511e2f195a947 100644 (file)
    message to a specific connection.  `conn', however, may be NULL.
    The `type' indicates the type of the message sent by the library.
    The application can for example filter the message according the
-   type. */
-
-static void
-silc_say(SilcClient client, SilcClientConnection conn,
-        SilcClientMessageType type, char *msg, ...)
-{
-
-}
-
+   type.  The variable argument list is arguments to the formatted
+   message that `msg' may be. */
+void silc_say(SilcClient client, SilcClientConnection conn,
+             SilcClientMessageType type, char *msg, ...);
 
 /* Message for a channel. The `sender' is the sender of the message
    The `channel' is the channel. The `message' is the message.  Note
    that `message' maybe NULL.  The `flags' indicates message flags
    and it is used to determine how the message can be interpreted
    (like it may tell the message is multimedia message). */
-
-static void
-silc_channel_message(SilcClient client, SilcClientConnection conn,
-                    SilcClientEntry sender, SilcChannelEntry channel,
-                    SilcMessagePayload payload, SilcChannelPrivateKey key,
-                    SilcMessageFlags flags, const unsigned char *message,
-                    SilcUInt32 message_len)
-{
-
-}
-
+void silc_channel_message(SilcClient client, SilcClientConnection conn,
+                         SilcClientEntry sender, SilcChannelEntry channel,
+                         SilcMessagePayload payload,
+                         SilcChannelPrivateKey key, SilcMessageFlags flags,
+                         const unsigned char *message,
+                         SilcUInt32 message_len);
 
 /* Private message to the client. The `sender' is the sender of the
    message. The message is `message'and maybe NULL.  The `flags'
    indicates message flags  and it is used to determine how the message
    can be interpreted (like it may tell the message is multimedia
    message). */
-
-static void
-silc_private_message(SilcClient client, SilcClientConnection conn,
-                    SilcClientEntry sender, SilcMessagePayload payload,
-                    SilcMessageFlags flags, const unsigned char *message,
-                    SilcUInt32 message_len)
-{
-
-}
-
+void silc_private_message(SilcClient client, SilcClientConnection conn,
+                         SilcClientEntry sender, SilcMessagePayload payload,
+                         SilcMessageFlags flags, const unsigned char *message,
+                         SilcUInt32 message_len);
 
 /* Notify message to the client. The notify arguments are sent in the
    same order as servers sends them. The arguments are same as received
@@ -65,32 +49,21 @@ silc_private_message(SilcClient client, SilcClientConnection conn,
    for channel the channel entry is sent to application (even if server
    does not send it because client library gets the channel entry from
    the Channel ID in the packet's header). */
-
-static void
-silc_notify(SilcClient client, SilcClientConnection conn,
-           SilcNotifyType type, ...)
-{
-
-}
-
-
-/* Command handler. This function is called always in the command function.
-   If error occurs it will be called as well. `conn' is the associated
-   client connection. `cmd_context' is the command context that was
-   originally sent to the command. `success' is FALSE if error occurred
-   during command. `command' is the command being processed. It must be
-   noted that this is not reply from server. This is merely called just
-   after application has called the command. Just to tell application
-   that the command really was processed. */
-
-static void
-silc_command(SilcClient client, SilcClientConnection conn,
-            SilcClientCommandContext cmd_context, SilcBool success,
-            SilcCommand command, SilcStatus status)
-{
-
-}
-
+void silc_notify(SilcClient client, SilcClientConnection conn,
+                SilcNotifyType type, ...);
+
+/* Command handler. This function is called always after application has
+   called a command.  It will be called to indicate that the command
+   was processed.  It will also be called if error occurs while processing
+   the command.  The `success' indicates whether the command was sent
+   or if error occurred.  The `status' indicates the actual error.
+   The `argc' and `argv' are the command line arguments sent to the
+   command by application.  Note that, this is not reply to the command
+   from server, this is merely and indication to application that the
+   command was processed. */
+void silc_command(SilcClient client, SilcClientConnection conn,
+                 SilcBool success, SilcCommand command, SilcStatus status,
+                 SilcUInt32 argc, unsigned char **argv);
 
 /* Command reply handler. This function is called always in the command reply
    function. If error occurs it will be called as well. Normal scenario
@@ -107,121 +80,61 @@ silc_command(SilcClient client, SilcClientConnection conn,
    the command reply status server returned. The `command' is the command
    reply being processed. The function has variable argument list and each
    command defines the number and type of arguments it passes to the
-   application (on error they are not sent). */
-
-static void
-silc_command_reply(SilcClient client, SilcClientConnection conn,
-                  SilcCommandPayload cmd_payload, SilcBool success,
-                  SilcCommand command, SilcStatus status, ...)
-{
-
-}
-
-
-/* Called to indicate that connection was either successfully established
-   or connecting failed.  This is also the first time application receives
-   the SilcClientConnection objecet which it should save somewhere.
-   If the `success' is FALSE the application must always call the function
-   silc_client_close_connection. */
-
-static void
-silc_connected(SilcClient client, SilcClientConnection conn,
-              SilcClientConnectionStatus status)
-{
-
-}
-
-
-/* Called to indicate that connection was disconnected to the server.
-   The `status' may tell the reason of the disconnection, and if the
-   `message' is non-NULL it may include the disconnection message
-   received from server. */
-
-static void
-silc_disconnected(SilcClient client, SilcClientConnection conn,
-                 SilcStatus status, const char *message)
-{
-
-}
+   application (on error they are not sent).
 
+   The arguments are sent in the same order as servers sends them.  The
+   arguments are same as received from the server except for ID's.  If
+   ID is received application receives the corresponding entry to the
+   ID. For example, if Client ID is receives application receives
+   SilcClientEntry. */
+void silc_command_reply(SilcClient client, SilcClientConnection conn,
+                       SilcCommand command, SilcStatus status,
+                       SilcStatus error, va_list ap);
 
 /* Find authentication method and authentication data by hostname and
-   port. The hostname may be IP address as well. When the authentication
-   method has been resolved the `completion' callback with the found
-   authentication method and authentication data is called. The `conn'
-   may be NULL. */
-
-static void
-silc_get_auth_method(SilcClient client, SilcClientConnection conn,
-                    char *hostname, SilcUInt16 port, SilcGetAuthMeth completion,
-                    void *context)
-{
-
-}
-
+   port. The hostname may be IP address as well. The `auth_method' is
+   the authentication method the remote connection requires.  It is
+   however possible that remote accepts also some other authentication
+   method.  Application should use the method that may have been
+   configured for this connection.  If none has been configured it should
+   use the required `auth_method'.  If the `auth_method' is
+   SILC_AUTH_NONE, server does not require any authentication or the
+   required authentication method is not known.  The `completion'
+   callback must be called to deliver the chosen authentication method
+   and data. The `conn' may be NULL. */
+void silc_get_auth_method(SilcClient client, SilcClientConnection conn,
+                         char *hostname, SilcUInt16 port,
+                         SilcAuthMethod auth_method,
+                         SilcGetAuthMeth completion, void *context);
 
 /* Verifies received public key. The `conn_type' indicates which entity
-   (server, client etc.) has sent the public key. If user decides to trust
-   the application may save the key as trusted public key for later
-   use. The `completion' must be called after the public key has been
-   verified. */
-
-static void
-silc_verify_public_key(SilcClient client, SilcClientConnection conn,
-                      SilcSocketType conn_type, unsigned char *pk,
-                      SilcUInt32 pk_len, SilcSKEPKType pk_type,
-                      SilcVerifyPublicKey completion, void *context)
-{
-
-}
-
+   (server or client) has sent the public key. If user decides to trust
+   the key the application may save the key as trusted public key for
+   later use. The `completion' must be called after the public key has
+   been verified. */
+void silc_verify_public_key(SilcClient client, SilcClientConnection conn,
+                           SilcConnectionType conn_type,
+                           SilcPublicKey public_key,
+                           SilcVerifyPublicKey completion, void *context);
 
 /* Ask (interact, that is) a passphrase from user. The passphrase is
    returned to the library by calling the `completion' callback with
    the `context'. The returned passphrase SHOULD be in UTF-8 encoded,
    if not then the library will attempt to encode. */
-
-static void
-silc_ask_passphrase(SilcClient client, SilcClientConnection conn,
-                   SilcAskPassphrase completion, void *context)
-{
-
-}
-
-
-/* Notifies application that failure packet was received.  This is called
-   if there is some protocol active in the client.  The `protocol' is the
-   protocol context.  The `failure' is opaque pointer to the failure
-   indication.  Note, that the `failure' is protocol dependant and
-   application must explicitly cast it to correct type.  Usually `failure'
-   is 32 bit failure type (see protocol specs for all protocol failure
-   types). */
-
-static void
-silc_failure(SilcClient client, SilcClientConnection conn,
-            SilcProtocol protocol, void *failure)
-{
-
-}
-
-
-/* Asks whether the user would like to perform the key agreement protocol.
-   This is called after we have received an key agreement packet or an
-   reply to our key agreement packet. This returns TRUE if the user wants
-   the library to perform the key agreement protocol and FALSE if it is not
-   desired (application may start it later by calling the function
-   silc_client_perform_key_agreement). If TRUE is returned also the
-   `completion' and `context' arguments must be set by the application. */
-
-static SilcBool
-silc_key_agreement(SilcClient client, SilcClientConnection conn,
-                  SilcClientEntry client_entry, const char *hostname,
-                  SilcUInt16 port, SilcKeyAgreementCallback *completion,
-                  void **context)
-{
-
-}
-
+void silc_ask_passphrase(SilcClient client, SilcClientConnection conn,
+                        SilcAskPassphrase completion, void *context);
+
+/* Called to indicate that incoming key agreement request has been
+   received.  If the application wants to perform key agreement it may
+   call silc_client_perform_key_agreement to initiate key agreementn or
+   silc_client_send_key_agreement to provide connection point to the
+   remote client in case the `hostname' is NULL.  If key agreement is
+   not desired this request can be ignored.  The `protocol' is either
+   value 0 for TCP or value 1 for UDP. */
+void silc_key_agreement(SilcClient client, SilcClientConnection conn,
+                       SilcClientEntry client_entry,
+                       const char *hostname, SilcUInt16 protocol,
+                       SilcUInt16 port);
 
 /* Notifies application that file transfer protocol session is being
    requested by the remote client indicated by the `client_entry' from
@@ -229,40 +142,9 @@ silc_key_agreement(SilcClient client, SilcClientConnection conn,
    session and it can be used to either accept or reject the file
    transfer request, by calling the silc_client_file_receive or
    silc_client_file_close, respectively. */
-
-static void
-silc_ftp(SilcClient client, SilcClientConnection conn,
-        SilcClientEntry client_entry, SilcUInt32 session_id,
-        const char *hostname, SilcUInt16 port)
-{
-
-}
-
-
-/* Delivers SILC session detachment data indicated by `detach_data' to the
-   application.  If application has issued SILC_COMMAND_DETACH command
-   the client session in the SILC network is not quit.  The client remains
-   in the network but is detached.  The detachment data may be used later
-   to resume the session in the SILC Network.  The appliation is
-   responsible of saving the `detach_data', to for example in a file.
-
-   The detachment data can be given as argument to the functions
-   silc_client_connect_to_server, or silc_client_add_connection when
-   creating connection to remote server, inside SilcClientConnectionParams
-   structure.  If it is provided the client library will attempt to resume
-   the session in the network.  After the connection is created
-   successfully, the application is responsible of setting the user
-   interface for user into the same state it was before detaching (showing
-   same channels, channel modes, etc).  It can do this by fetching the
-   information (like joined channels) from the client library. */
-
-static void
-silc_detach(SilcClient client, SilcClientConnection conn,
-           const unsigned char *detach_data, SilcUInt32 detach_data_len)
-{
-
-}
-
+void silc_ftp(SilcClient client, SilcClientConnection conn,
+             SilcClientEntry client_entry, SilcUInt32 session_id,
+             const char *hostname, SilcUInt16 port);
 
 /* The SilcClientOperation structure containing the operation functions.
    You will give this as an argument to silc_client_alloc function. */
@@ -273,13 +155,9 @@ SilcClientOperations ops = {
   silc_notify,
   silc_command,
   silc_command_reply,
-  silc_connected,
-  silc_disconnected,
   silc_get_auth_method,
   silc_verify_public_key,
   silc_ask_passphrase,
-  silc_failure,
   silc_key_agreement,
-  silc_ftp,
-  silc_detach
+  silc_ftp
 };
diff --git a/lib/silcclient/client_resume.c b/lib/silcclient/client_resume.c
deleted file mode 100644 (file)
index dbad9c7..0000000
+++ /dev/null
@@ -1,429 +0,0 @@
-/*
-
-  client_resume.c
-
-  Author: Pekka Riikonen <priikone@silcnet.org>
-
-  Copyright (C) 2002, 2004, 2006 Pekka Riikonen
-
-  This program is free software; you can redistribute it and/or modify
-  it under the terms of the GNU General Public License as published by
-  the Free Software Foundation; version 2 of the License.
-
-  This program is distributed in the hope that it will be useful,
-  but WITHOUT ANY WARRANTY; without even the implied warranty of
-  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-  GNU General Public License for more details.
-
-*/
-/* $Id$ */
-
-#include "silc.h"
-#include "silcclient.h"
-#include "client_internal.h"
-
-SILC_CLIENT_CMD_REPLY_FUNC(resume);
-SILC_CLIENT_CMD_FUNC(resume_identify);
-SILC_CLIENT_CMD_FUNC(resume_cmode);
-SILC_CLIENT_CMD_FUNC(resume_users);
-
-#define RESUME_CALL_COMPLETION(client, session, s)                     \
-do {                                                                   \
-  SILC_LOG_DEBUG(("Calling completion"));                              \
-  session->success = s;                                                        \
-  silc_schedule_task_add(client->schedule, 0,                          \
-                        silc_client_resume_call_completion, session,   \
-                        0, 1, SILC_TASK_TIMEOUT, SILC_TASK_PRI_LOW);   \
-} while(0)
-
-/* Generic command reply callback. */
-
-SILC_CLIENT_CMD_REPLY_FUNC(resume)
-{
-  SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
-  SILC_LOG_DEBUG(("Start"));
-  SILC_CLIENT_PENDING_EXEC(cmd, silc_command_get(cmd->payload));
-}
-
-/* Special command reply callback for IDENTIFY callbacks.  This calls
-   the pending callback for every returned command entry. */
-
-SILC_CLIENT_CMD_REPLY_FUNC(resume_special)
-{
-  SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
-  int i;
-
-  SILC_LOG_DEBUG(("Start"));
-  for (i = 0; i < cmd->callbacks_count; i++)
-    if (cmd->callbacks[i].callback)
-      (*cmd->callbacks[i].callback)(cmd->callbacks[i].context, cmd);
-}
-
-/* Completion calling callback */
-
-SILC_TASK_CALLBACK(silc_client_resume_call_completion)
-{
-  SilcClientResumeSession session = context;
-  int i;
-
-  SILC_LOG_DEBUG(("Session completed"));
-
-  for (i = 0; i < session->cmd_idents_count; i++)
-    silc_client_command_pending_del(session->conn, SILC_COMMAND_IDENTIFY,
-                                   session->cmd_idents[i]);
-  silc_free(session->cmd_idents);
-
-  session->callback(session->client, session->conn, session->success,
-                   session->context);
-
-  memset(session, 'F', sizeof(*session));
-  silc_free(session);
-}
-
-/* This function is used to perform the resuming procedure after the
-   client has connected to the server properly and has received the
-   Client ID for the resumed session.  This resolves all channels
-   that the resumed client is joined, joined users, users modes
-   and channel modes.  The `callback' is called after this procedure
-   is completed. */
-
-void silc_client_resume_session(SilcClient client,
-                               SilcClientConnection conn,
-                               SilcClientResumeSessionCallback callback,
-                               void *context)
-{
-  SilcClientResumeSession session;
-  SilcIDCacheList list;
-  SilcIDCacheEntry entry;
-  SilcChannelEntry channel;
-  SilcBuffer tmp;
-  int i;
-  SilcBool ret;
-
-  SILC_LOG_DEBUG(("Resuming detached session"));
-
-  session = silc_calloc(1, sizeof(*session));
-  if (!session) {
-    callback(client, conn, FALSE, context);
-    return;
-  }
-  session->client = client;
-  session->conn = conn;
-  session->callback = callback;
-  session->context = context;
-
-  /* First, send UMODE commandto get our own user mode in the network */
-  SILC_LOG_DEBUG(("Sending UMODE"));
-  tmp = silc_id_payload_encode(conn->local_entry->id, SILC_ID_CLIENT);
-  silc_client_command_send(client, conn, SILC_COMMAND_UMODE,
-                          conn->cmd_ident, 1, 1, tmp->data, tmp->len);
-  silc_buffer_free(tmp);
-
-  /* Second, send IDENTIFY command of all channels we know about.  These
-     are the channels we've joined to according our detachment data. */
-  if (silc_idcache_get_all(conn->internal->channel_cache, &list)) {
-    unsigned char **res_argv = NULL;
-    SilcUInt32 *res_argv_lens = NULL, *res_argv_types = NULL, res_argc = 0;
-
-    session->channel_count = silc_idcache_list_count(list);
-
-    ret = silc_idcache_list_first(list, &entry);
-    while (ret) {
-      channel = entry->context;
-      tmp = silc_id_payload_encode(channel->id, SILC_ID_CHANNEL);
-      res_argv = silc_realloc(res_argv, sizeof(*res_argv) * (res_argc + 1));
-      res_argv_lens = silc_realloc(res_argv_lens, sizeof(*res_argv_lens) *
-                                  (res_argc + 1));
-      res_argv_types = silc_realloc(res_argv_types, sizeof(*res_argv_types) *
-                                   (res_argc + 1));
-      res_argv[res_argc] = silc_memdup(tmp->data, tmp->len);
-      res_argv_lens[res_argc] = tmp->len;
-      res_argv_types[res_argc] = res_argc + 5;
-      res_argc++;
-      silc_buffer_free(tmp);
-      ret = silc_idcache_list_next(list, &entry);
-    }
-    silc_idcache_list_free(list);
-
-    if (res_argc) {
-      /* Send the IDENTIFY command */
-      SILC_LOG_DEBUG(("Sending IDENTIFY"));
-      silc_client_command_register(client, SILC_COMMAND_IDENTIFY, NULL, NULL,
-                                  silc_client_command_reply_resume_special,
-                                  0, ++conn->cmd_ident);
-      silc_client_command_pending(conn, SILC_COMMAND_IDENTIFY,
-                                 conn->cmd_ident,
-                                 silc_client_command_resume_identify,
-                                 session);
-
-      tmp = silc_command_payload_encode(SILC_COMMAND_IDENTIFY,
-                                       res_argc, res_argv, res_argv_lens,
-                                       res_argv_types, conn->cmd_ident);
-      silc_client_packet_send(client, conn->sock, SILC_PACKET_COMMAND,
-                             NULL, 0, NULL, NULL, tmp->data, tmp->len, TRUE);
-
-      session->cmd_idents = silc_realloc(session->cmd_idents,
-                                        sizeof(*session->cmd_idents) *
-                                        (session->cmd_idents_count + 1));
-      session->cmd_idents[session->cmd_idents_count] = conn->cmd_ident;
-      session->cmd_idents_count++;
-
-      for (i = 0; i < res_argc; i++)
-       silc_free(res_argv[i]);
-      silc_free(res_argv);
-      silc_free(res_argv_lens);
-      silc_free(res_argv_types);
-      silc_buffer_free(tmp);
-    }
-  }
-
-  if (!session->channel_count)
-    RESUME_CALL_COMPLETION(client, session, TRUE);
-
-  /* Now, we wait for replies to come back and then continue with USERS,
-     CMODE and TOPIC commands. */
-}
-
-/* Received identify reply for a channel entry */
-
-SILC_CLIENT_CMD_FUNC(resume_identify)
-{
-  SilcClientResumeSession session = context;
-  SilcClientCommandReplyContext cmd = context2;
-  SilcClient client = session->client;
-  SilcClientConnection conn = session->conn;
-  unsigned char *tmp;
-  SilcUInt32 tmp_len;
-  SilcChannelEntry channel = NULL;
-  SilcChannelID *channel_id;
-  SilcIDPayload idp;
-  SilcIdType id_type;
-
-  SILC_LOG_DEBUG(("Start"));
-
-  tmp = silc_argument_get_arg_type(cmd->args, 2, &tmp_len);
-  if (!tmp)
-    goto err;
-
-  if (cmd->error != SILC_STATUS_OK) {
-    /* Delete unknown channel from our cache */
-    if (cmd->error == SILC_STATUS_ERR_NO_SUCH_CHANNEL_ID) {
-      channel_id = silc_id_payload_parse_id(tmp, tmp_len, NULL);
-      if (channel_id) {
-       channel = silc_client_get_channel_by_id(client, conn, channel_id);
-       if (channel)
-         silc_client_del_channel(client, conn, channel);
-       silc_free(channel_id);
-      }
-    }
-    goto err;
-  }
-
-  idp = silc_id_payload_parse(tmp, tmp_len);
-  if (!idp) {
-    return;
-  }
-  id_type = silc_id_payload_get_type(idp);
-
-  switch (id_type) {
-  case SILC_ID_CHANNEL:
-    channel_id = silc_id_payload_get_id(idp);
-    channel = silc_client_get_channel_by_id(client, conn, channel_id);
-    silc_free(channel_id);
-    break;
-  default:
-    silc_id_payload_free(idp);
-    goto err;
-    break;
-  }
-
-  /* Now, send CMODE command for this channel.  We send only this one
-     because this will return also error if we are not currently joined
-     on this channel, plus we get the channel mode.  USERS and TOPIC
-     commands are called after this returns. */
-  if (channel) {
-    SILC_LOG_DEBUG(("Sending CMODE"));
-    silc_client_command_register(client, SILC_COMMAND_CMODE, NULL, NULL,
-                                silc_client_command_reply_resume, 0,
-                                ++conn->cmd_ident);
-    silc_client_command_send(client, conn, SILC_COMMAND_CMODE,
-                            conn->cmd_ident, 1, 1, tmp, tmp_len);
-    silc_client_command_pending(conn, SILC_COMMAND_CMODE, conn->cmd_ident,
-                               silc_client_command_resume_cmode, session);
-  }
-
-  silc_id_payload_free(idp);
-
-  if (cmd->status != SILC_STATUS_OK &&
-      cmd->status != SILC_STATUS_LIST_END)
-    return;
-
-  /* Unregister this command reply */
-  silc_client_command_unregister(client, SILC_COMMAND_IDENTIFY, NULL,
-                                silc_client_command_reply_resume,
-                                cmd->ident);
-  return;
-
- err:
-  session->channel_count--;
-  if (!session->channel_count)
-    RESUME_CALL_COMPLETION(client, session, FALSE);
-}
-
-/* Received cmode to channel entry */
-
-SILC_CLIENT_CMD_FUNC(resume_cmode)
-{
-  SilcClientResumeSession session = context;
-  SilcClientCommandReplyContext cmd = context2;
-  SilcClient client = session->client;
-  SilcClientConnection conn = session->conn;
-  unsigned char *tmp;
-  SilcChannelID *channel_id;
-  SilcChannelEntry channel;
-  SilcUInt32 len;
-
-  SILC_LOG_DEBUG(("Start"));
-
-  /* Unregister this command reply */
-  silc_client_command_unregister(client, SILC_COMMAND_CMODE, NULL,
-                                silc_client_command_reply_resume,
-                                cmd->ident);
-
-  if (cmd->error != SILC_STATUS_OK)
-    goto err;
-
-  /* Take Channel ID */
-  tmp = silc_argument_get_arg_type(cmd->args, 2, &len);
-  if (!tmp)
-    goto err;
-  channel_id = silc_id_payload_parse_id(tmp, len, NULL);
-  if (!channel_id)
-    goto err;
-
-  /* Get the channel entry */
-  channel = silc_client_get_channel_by_id(cmd->client, conn, channel_id);
-  if (channel) {
-
-    /* Get channel mode */
-    tmp = silc_argument_get_arg_type(cmd->args, 3, NULL);
-    if (tmp)
-      SILC_GET32_MSB(channel->mode, tmp);
-
-    tmp = silc_argument_get_arg_type(cmd->args, 2, &len);
-
-    /* And now, we will send USERS to get users on the channel */
-    SILC_LOG_DEBUG(("Sending USERS"));
-    silc_client_command_register(client, SILC_COMMAND_USERS, NULL, NULL,
-                                silc_client_command_reply_users_i, 0,
-                                ++conn->cmd_ident);
-    silc_client_command_send(client, conn, SILC_COMMAND_USERS,
-                            conn->cmd_ident, 1, 1, tmp, len);
-    silc_client_command_pending(conn, SILC_COMMAND_USERS, conn->cmd_ident,
-                               silc_client_command_resume_users, session);
-  }
-
-  silc_free(channel_id);
-  return;
-
- err:
-  session->channel_count--;
-  if (!session->channel_count)
-    RESUME_CALL_COMPLETION(client, session, FALSE);
-}
-
-/* Received users reply to a channel entry */
-
-SILC_CLIENT_CMD_FUNC(resume_users)
-{
-  SilcClientResumeSession session = context;
-  SilcClientCommandReplyContext cmd = context2;
-  SilcClient client = session->client;
-  SilcClientConnection conn = session->conn;
-  SilcBufferStruct client_id_list, client_mode_list;
-  unsigned char *tmp;
-  SilcUInt32 tmp_len, list_count;
-  SilcChannelEntry channel;
-  SilcChannelID *channel_id = NULL;
-
-  SILC_LOG_DEBUG(("Start"));
-
-  /* Unregister this command reply */
-  silc_client_command_unregister(client, SILC_COMMAND_USERS, NULL,
-                                silc_client_command_reply_users_i,
-                                cmd->ident);
-
-  if (cmd->error != SILC_STATUS_OK)
-    goto err;
-
-  /* Get channel ID */
-  tmp = silc_argument_get_arg_type(cmd->args, 2, &tmp_len);
-  if (!tmp) {
-    COMMAND_REPLY_ERROR(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
-    goto err;
-  }
-  channel_id = silc_id_payload_parse_id(tmp, tmp_len, NULL);
-  if (!channel_id) {
-    COMMAND_REPLY_ERROR(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
-    goto err;
-  }
-
-  /* Get the list count */
-  tmp = silc_argument_get_arg_type(cmd->args, 3, &tmp_len);
-  if (!tmp) {
-    COMMAND_REPLY_ERROR(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
-    goto err;
-  }
-  SILC_GET32_MSB(list_count, tmp);
-
-  /* Get Client ID list */
-  tmp = silc_argument_get_arg_type(cmd->args, 4, &tmp_len);
-  if (!tmp) {
-    COMMAND_REPLY_ERROR(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
-    goto err;
-  }
-  silc_buffer_set(&client_id_list, tmp, tmp_len);
-
-  /* Get client mode list */
-  tmp = silc_argument_get_arg_type(cmd->args, 5, &tmp_len);
-  if (!tmp) {
-    COMMAND_REPLY_ERROR(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
-    goto err;
-  }
-  silc_buffer_set(&client_mode_list, tmp, tmp_len);
-
-  /* Get channel entry */
-  channel = silc_client_get_channel_by_id(cmd->client, conn, channel_id);
-  if (!channel)
-    goto err;
-
-  /* Send fake JOIN command reply to application */
-  client->internal->ops->command_reply(client, conn, cmd->payload, TRUE,
-                                      SILC_COMMAND_JOIN, cmd->status,
-                                      channel->channel_name, channel,
-                                      channel->mode, 0,
-                                      NULL, NULL, NULL, NULL,
-                                      channel->hmac, list_count,
-                                      &client_id_list, client_mode_list);
-
-  /* Send TOPIC for this channel to get the topic */
-  SILC_LOG_DEBUG(("Sending TOPIC"));
-  tmp = silc_argument_get_arg_type(cmd->args, 2, &tmp_len);
-  silc_client_command_send(client, conn, SILC_COMMAND_TOPIC,
-                          ++conn->cmd_ident, 1, 1, tmp, tmp_len);
-
-  /* Call the completion callback after we've got reply to all of
-     our channels */
-  session->channel_count--;
-  if (!session->channel_count)
-    RESUME_CALL_COMPLETION(client, session, TRUE);
-
-  silc_free(channel_id);
-  return;
-
- err:
-  silc_free(channel_id);
-  session->channel_count--;
-  if (!session->channel_count)
-    RESUME_CALL_COMPLETION(client, session, FALSE);
-}
index 72a9c38fc08e1d6a5e9107d9392faab905fa3b4c..a5ba076ec23cb79c7132c29ef04ed93576303cfa 100644 (file)
@@ -390,7 +390,7 @@ SilcUInt16 silc_client_command_call(SilcClient client,
   char *arg;
 
   if (!conn) {
-    client->internal->ops->say(client, NULL, SILC_CLIENT_MESSAGE_ERROR,
+    client->internal->ops->say(client, NULL, SILC_CLIENT_MESSAGE_COMMAND_ERROR,
       "You are not connected to a server, please connect to server");
     return 0;
   }
@@ -670,7 +670,7 @@ SILC_FSM_STATE(silc_client_command_whois)
     SilcPublicKey pk;
 
     if (!silc_pkcs_load_public_key(pubkey, &pk)) {
-      SAY(client, conn, SILC_CLIENT_MESSAGE_ERROR,
+      SAY(client, conn, SILC_CLIENT_MESSAGE_COMMAND_ERROR,
          "Could not load public key %s, check the filename",
          pubkey);
       COMMAND_ERROR(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
@@ -1349,7 +1349,7 @@ SILC_FSM_STATE(silc_client_command_join)
        }
        if (!silc_load_key_pair(cmd->argv[i + 1], cmd->argv[i + 2], pass,
                                &pubkey, &privkey)) {
-         SAY(conn->client, conn, SILC_CLIENT_MESSAGE_ERROR,
+         SAY(conn->client, conn, SILC_CLIENT_MESSAGE_COMMAND_ERROR,
              "Could not load key pair, check your arguments");
          COMMAND_ERROR(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
          goto out;
@@ -1769,7 +1769,7 @@ SILC_FSM_STATE(silc_client_command_cmode)
            pass = cmd->argv[5];
          if (!silc_load_key_pair(cmd->argv[3], cmd->argv[4], pass,
                                  &pubkey, &privkey)) {
-           SAY(client, conn, SILC_CLIENT_MESSAGE_ERROR,
+           SAY(client, conn, SILC_CLIENT_MESSAGE_COMMAND_ERROR,
                "Could not load key pair, check your arguments");
            COMMAND_ERROR(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
            goto out;
@@ -1821,7 +1821,7 @@ SILC_FSM_STATE(silc_client_command_cmode)
          if (cmd->argv[k][0] == '+')
            chadd = TRUE;
          if (!silc_pkcs_load_public_key(cmd->argv[k] + 1, &chpk)) {
-           SAY(conn->client, conn, SILC_CLIENT_MESSAGE_ERROR,
+           SAY(conn->client, conn, SILC_CLIENT_MESSAGE_COMMAND_ERROR,
                "Could not load public key %s, check the filename",
                cmd->argv[k]);
            COMMAND_ERROR(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
@@ -1983,7 +1983,7 @@ SILC_FSM_STATE(silc_client_command_cumode)
            pass = cmd->argv[6];
          if (!silc_load_key_pair(cmd->argv[4], cmd->argv[5], pass,
                                  &pubkey, &privkey)) {
-           SAY(conn->client, conn, SILC_CLIENT_MESSAGE_ERROR,
+           SAY(conn->client, conn, SILC_CLIENT_MESSAGE_COMMAND_ERROR,
                "Could not load key pair, check your arguments");
            COMMAND_ERROR(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
            goto out;
@@ -2436,7 +2436,7 @@ SILC_FSM_STATE(silc_client_command_watch)
     SilcBuffer buffer;
 
     if (!silc_pkcs_load_public_key(pubkey, &pk)) {
-      SAY(conn->client, conn, SILC_CLIENT_MESSAGE_ERROR,
+      SAY(conn->client, conn, SILC_CLIENT_MESSAGE_COMMAND_ERROR,
          "Could not load public key %s, check the filename", pubkey);
       COMMAND_ERROR(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
       goto out;
index 80e85186e2b5988dedfe1368889eb4a6b96fa791..73e45893ef9f4011f0eb460b2b0b74453ba9c2f0 100644 (file)
@@ -42,7 +42,7 @@ do {                                                          \
   SILC_LOG_DEBUG(("%s", silc_get_command_name(cmd->cmd)));             \
   if (cmd->error != SILC_STATUS_OK) {                                  \
     if (cmd->verbose)                                                  \
-      SAY(cmd->conn->client, cmd->conn, SILC_CLIENT_MESSAGE_ERROR,     \
+      SAY(cmd->conn->client, cmd->conn, SILC_CLIENT_MESSAGE_COMMAND_ERROR, \
          msg "%s", silc_get_status_message(cmd->error));               \
     ERROR_CALLBACK(cmd->error);                                                \
     silc_client_command_process_error(cmd, state_context, cmd->error); \
@@ -199,7 +199,10 @@ SILC_FSM_STATE(silc_client_command_reply)
     return SILC_FSM_FINISH;
   }
 
-  /* Signal command thread that command reply has arrived */
+  /* Signal command thread that command reply has arrived.  We continue
+     command reply processing synchronously because we save the command
+     payload into state context.  No other reply may arrive to this command
+     while we're processing this reply. */
   silc_fsm_set_state_context(&cmd->thread, payload);
   silc_fsm_next(&cmd->thread, silc_client_command_reply_process);
   silc_fsm_continue_sync(&cmd->thread);
@@ -1217,13 +1220,12 @@ SILC_FSM_STATE(silc_client_command_reply_join)
 
     /* Get client entry */
     client_entry = silc_client_get_client_by_id(client, conn, &id.u.client_id);
-    if (!client_entry || !client_entry->internal.valid)
-      continue;
-
-    /* Join client to the channel */
-    silc_rwlock_wrlock(client_entry->internal.lock);
-    silc_client_add_to_channel(client, conn, channel, client_entry, mode);
-    silc_rwlock_unlock(client_entry->internal.lock);
+    if (client_entry && client_entry->internal.valid) {
+      /* Join client to the channel */
+      silc_rwlock_wrlock(client_entry->internal.lock);
+      silc_client_add_to_channel(client, conn, channel, client_entry, mode);
+      silc_rwlock_unlock(client_entry->internal.lock);
+    }
     silc_client_unref_client(client, conn, client_entry);
 
     if (!silc_buffer_pull(&client_id_list, idp_len)) {
@@ -1241,7 +1243,7 @@ SILC_FSM_STATE(silc_client_command_reply_join)
   if (hmac) {
     if (!silc_hmac_alloc(hmac, NULL, &channel->internal.hmac)) {
       if (cmd->verbose)
-       SAY(client, conn, SILC_CLIENT_MESSAGE_ERROR,
+       SAY(client, conn, SILC_CLIENT_MESSAGE_COMMAND_ERROR,
            "Cannot join channel: Unsupported HMAC `%s'", hmac);
       ERROR_CALLBACK(SILC_STATUS_ERR_UNKNOWN_ALGORITHM);
       silc_rwlock_unlock(channel->internal.lock);
index 346a166a57ee7214046586ac471ecc78c30e581f..4d3fc3a05628b15925bbf648ff88bd68e65400c7 100644 (file)
@@ -22,7 +22,7 @@
  * DESCRIPTION
  *
  * This interface defines the SILC Client Library API for the application.
- * The Client Library is a full features SILC client without a user interface.
+ * The Client Library is a full featured SILC client without user interface.
  * A simple interface called SILC Client Operations (SilcClientOperations)
  * is provided for applications to implmeent the necessary functions to use
  * the client library.  The silcclient.h header file includes client library
@@ -37,7 +37,7 @@
  * multiple connections in one SilcClient.  Connections can be created to
  * servers and other clients.
  *
- * The Client Library support multiple threads and is threads safe is used
+ * The Client Library support multiple threads and is threads safe if used
  * correctly.  Messages can be sent from multiple threads without any
  * locking.  Messages however are always received only in one thread unless
  * message waiting (see silc_client_private_message_wait as an example) is
@@ -159,6 +159,13 @@ typedef void (*SilcClientStopped)(SilcClient client, void *context);
  *    indicate the reason for disconnection.  If the `message' is non-NULL
  *    it delivers error or disconnection message.
  *
+ *    The `conn' is the connection to the remote host.  In case error
+ *    occurred the `conn' may be NULL, however, in some cases a valid `conn'
+ *    is returned even in error.  If `conn' is non-NULL the receiver is
+ *    responsible of closing the connection with silc_client_close_connection
+ *    function, except when SILC_CLINET_CONN_DISCONNECTED or some error
+ *    was received.  In these cases the library will close the connection.
+ *
  ***/
 typedef void (*SilcClientConnectCallback)(SilcClient client,
                                          SilcClientConnection conn,
@@ -482,6 +489,7 @@ typedef enum {
   SILC_CLIENT_MESSAGE_INFO,           /* Informational */
   SILC_CLIENT_MESSAGE_WARNING,        /* Warning */
   SILC_CLIENT_MESSAGE_ERROR,          /* Error */
+  SILC_CLIENT_MESSAGE_COMMAND_ERROR,   /* Error during command */
   SILC_CLIENT_MESSAGE_AUDIT,          /* Auditable */
 } SilcClientMessageType;
 /***/
@@ -1985,15 +1993,39 @@ SilcBool silc_client_set_away_message(SilcClient client,
  *    File transmission session status types.  These will indicate
  *    the status of the file transmission session.
  *
+ *    The SILC_CLIENT_FILE_MONITOR_KEY_AGREEMENT is called when session
+ *    is key exchange phase.
+ *
+ *    The SILC_CLIENT_FILE_MONITOR_SEND is called when data is being sent
+ *    to remote client.
+ *
+ *    The SILC_CLIENT_FILE_MONITOR_RECEIVE is called when data is being
+ *    recieved from remote client.
+ *
+ *    The SILC_CLIENT_FILE_MONITOR_CLOSED will be called when the user
+ *    issues silc_client_file_close.  If needed, it may be ignored in the
+ *    monitor callback.
+ *
+ *    The SILC_CLIENT_FILE_MONITOR_DISCONNECT will be called if remote
+ *    disconnects the session connection.  The silc_client_file_close must
+ *    be called when this status is received.  The session is over when 
+ *    this is received.
+ *
+ *    The SILC_CLIENLT_FILE_MONITOR_ERROR is called in case some error
+ *    occured.  The SilcClientFileError will indicate more detailed error
+ *    condition.  The silc_client_file_close must be called when this status
+ *    is received.  The session is over when this is received.
+ *
  * SOURCE
  */
 typedef enum {
   SILC_CLIENT_FILE_MONITOR_KEY_AGREEMENT,    /* In key agreemenet phase */
   SILC_CLIENT_FILE_MONITOR_SEND,            /* Sending file */
   SILC_CLIENT_FILE_MONITOR_RECEIVE,         /* Receiving file */
-  SILC_CLIENT_FILE_MONITOR_GET,
-  SILC_CLIENT_FILE_MONITOR_PUT,
+  SILC_CLIENT_FILE_MONITOR_GET,                     /* Unsupported */
+  SILC_CLIENT_FILE_MONITOR_PUT,                     /* Unsupported */
   SILC_CLIENT_FILE_MONITOR_CLOSED,          /* Session closed */
+  SILC_CLIENT_FILE_MONITOR_DISCONNECT,      /* Session disconnected */
   SILC_CLIENT_FILE_MONITOR_ERROR,           /* Error during session */
 } SilcClientMonitorStatus;
 /***/
@@ -2014,12 +2046,15 @@ typedef enum {
  */
 typedef enum {
   SILC_CLIENT_FILE_OK,
-  SILC_CLIENT_FILE_ERROR,
-  SILC_CLIENT_FILE_UNKNOWN_SESSION,
-  SILC_CLIENT_FILE_ALREADY_STARTED,
-  SILC_CLIENT_FILE_NO_SUCH_FILE,
-  SILC_CLIENT_FILE_PERMISSION_DENIED,
-  SILC_CLIENT_FILE_KEY_AGREEMENT_FAILED,
+  SILC_CLIENT_FILE_ERROR,                   /* Generic error */
+  SILC_CLIENT_FILE_UNKNOWN_SESSION,         /* Unknown session ID */
+  SILC_CLIENT_FILE_ALREADY_STARTED,         /* Session already started */
+  SILC_CLIENT_FILE_NO_SUCH_FILE,            /* No such file */
+  SILC_CLIENT_FILE_PERMISSION_DENIED,       /* Permission denied */
+  SILC_CLIENT_FILE_KEY_AGREEMENT_FAILED,     /* Key exchange failed */
+  SILC_CLIENT_FILE_CONNECT_FAILED,          /* Error during connecting */
+  SILC_CLIENT_FILE_TIMEOUT,                 /* Connecting timedout */
+  SILC_CLIENT_FILE_NO_MEMORY,               /* System out of memory */
 } SilcClientFileError;
 /***/
 
@@ -2047,7 +2082,8 @@ typedef enum {
  *    currently transmitted amount of total `filesize'.  The `client_entry'
  *    indicates the remote client, and the transmission session ID is the
  *    `session_id'.  The filename being transmitted is indicated by the
- *    `filepath'.
+ *    `filepath'.  The `conn' is NULL if the connection to remote client
+ *    does not exist yet.
  *
  ***/
 typedef void (*SilcClientFileMonitor)(SilcClient client,
@@ -2154,6 +2190,7 @@ typedef void (*SilcClientFileAskName)(SilcClient client,
  ***/
 SilcClientFileError
 silc_client_file_send(SilcClient client,
+                     SilcClientConnection conn,
                      SilcClientEntry client_entry,
                      SilcClientConnectionParams *params,
                      SilcPublicKey public_key,
@@ -2199,6 +2236,9 @@ silc_client_file_send(SilcClient client,
 SilcClientFileError
 silc_client_file_receive(SilcClient client,
                         SilcClientConnection conn,
+                        SilcClientConnectionParams *params,
+                        SilcPublicKey public_key,
+                        SilcPrivateKey private_key,
                         SilcClientFileMonitor monitor,
                         void *monitor_context,
                         const char *path,
index 0f4e996a8572955a0576ef8bd308862a782459da..aae2c0442fb05563439f298401a49f69a17aacae 100644 (file)
@@ -49,7 +49,7 @@
  *    it should always duplicated them.
  *
  *    None of the string arrays are set if the first character is '\0'.
- *    All string arrays are always NULL terminated.
+ *    All string arrays are always zero ('\0') terminated.
  *
  *    If application stores the SilcClientEntry it must always take
  *    a reference of it by calling silc_client_ref_client function.  The
@@ -450,46 +450,6 @@ void silc_client_get_clients_by_channel(SilcClient client,
                                        SilcGetClientCallback completion,
                                        void *context);
 
-/****f* silcclient/SilcClientAPI/silc_client_get_clients_by_list
- *
- * SYNOPSIS
- *
- *    SilcUInt16
- *    silc_client_get_clients_by_list(SilcClient client,
- *                                    SilcClientConnection conn,
- *                                    SilcUInt32 list_count,
- *                                    SilcBuffer client_id_list,
- *                                    SilcGetClientCallback completion,
- *                                    void *context);
- *
- * DESCRIPTION
- *
- *    Gets client entries by the list of client ID's `client_id_list'. This
- *    always resolves those client ID's it doesn't know about from the server.
- *    The `client_id_list' is a list of ID Payloads added one after other.
- *    JOIN command reply and USERS command reply for example returns this sort
- *    of list. The `completion' will be called after the entries are available.
- *    When server returns the client information it will be cached and can be
- *    accessed locally at a later time.  The resolving is done with WHOIS
- *    command.
- *
- *    Returns command identifier for the resolving.  It can be used to attach
- *    a pending command to it, if needed.  Returns 0 when no resolving was
- *    done or wasn't needed (completion is called before this returns).
- *
- * NOTES
- *
- *    If even after resolving some Client ID in the `client_id_list' is
- *    unknown it will be ignored and error is not returned.
- *
- ***/
-SilcUInt16 silc_client_get_clients_by_list(SilcClient client,
-                                          SilcClientConnection conn,
-                                          SilcUInt32 list_count,
-                                          SilcBuffer client_id_list,
-                                          SilcGetClientCallback completion,
-                                          void *context);
-
 /****f* silcclient/SilcClientAPI/silc_client_get_client_by_id
  *
  * SYNOPSIS