/* SYNTAX: PING */
/* SYNTAX: SCONNECT <server> [<port>] */
/* SYNTAX: USERS <channel> */
-/* SYNTAX: FILE SEND <filename> <nickname> */
+/* SYNTAX: FILE SEND <filepath> <nickname> */
/* SYNTAX: FILE RECEIVE [<nickname>] */
/* SYNTAX: FILE CLOSE [<nickname>] */
-/* SYNTAX: FILE SHOW */
+/* SYNTAX: FILE */
void silc_command_exec(SILC_SERVER_REC *server,
const char *command, const char *args)
if (ftp->client_entry == client_entry) {
ftp->session_id = session_id;
- if (!ftp->filepath)
+ if (!ftp->filepath && filepath)
ftp->filepath = strdup(filepath);
break;
}
}
+ if (ftp == SILC_LIST_END)
+ return;
+
+ if (status == SILC_CLIENT_FILE_MONITOR_ERROR) {
+ printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
+ SILCTXT_FILE_ERROR, client_entry->nickname);
+ silc_schedule_task_add(silc_client->schedule, 0,
+ silc_client_file_close_later, ftp,
+ 1, 0, SILC_TASK_TIMEOUT, SILC_TASK_PRI_NORMAL);
+ if (ftp == server->current_session)
+ server->current_session = NULL;
+ silc_dlist_del(server->ftp_sessions, ftp);
+ }
+
+ if (status == SILC_CLIENT_FILE_MONITOR_KEY_AGREEMENT) {
+ printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
+ SILCTXT_FILE_KEY_EXCHANGE, client_entry->nickname);
+ }
+
if (status == SILC_CLIENT_FILE_MONITOR_SEND) {
- if (offset == 0)
+ if (offset == 0) {
printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
SILCTXT_FILE_TRANSMIT, filepath, fsize,
client_entry->nickname);
+ ftp->starttime = time(NULL);
+ }
if (offset == filesize) {
printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
SILCTXT_FILE_TRANSMITTED, filepath, fsize,
- client_entry->nickname, "1024");
- if (ftp != SILC_LIST_END) {
- silc_schedule_task_add(silc_client->schedule, 0,
- silc_client_file_close_later, ftp,
- 1, 0, SILC_TASK_TIMEOUT, SILC_TASK_PRI_NORMAL);
- silc_dlist_del(server->ftp_sessions, ftp);
- }
+ client_entry->nickname, ftp->kps);
+ silc_schedule_task_add(silc_client->schedule, 0,
+ silc_client_file_close_later, ftp,
+ 1, 0, SILC_TASK_TIMEOUT, SILC_TASK_PRI_NORMAL);
+ if (ftp == server->current_session)
+ server->current_session = NULL;
+ silc_dlist_del(server->ftp_sessions, ftp);
}
}
if (status == SILC_CLIENT_FILE_MONITOR_RECEIVE) {
- if (offset == 0)
+ if (offset == 0) {
printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
SILCTXT_FILE_RECEIVE, filepath, fsize,
client_entry->nickname);
+ ftp->starttime = time(NULL);
+ }
+
if (offset == filesize) {
printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
SILCTXT_FILE_RECEIVED, filepath, fsize,
- client_entry->nickname, "1024");
- if (ftp != SILC_LIST_END) {
- silc_schedule_task_add(silc_client->schedule, 0,
- silc_client_file_close_later, ftp,
- 1, 0, SILC_TASK_TIMEOUT, SILC_TASK_PRI_NORMAL);
- silc_dlist_del(server->ftp_sessions, ftp);
- }
+ client_entry->nickname, ftp->kps);
+ silc_schedule_task_add(silc_client->schedule, 0,
+ silc_client_file_close_later, ftp,
+ 1, 0, SILC_TASK_TIMEOUT, SILC_TASK_PRI_NORMAL);
+ if (ftp == server->current_session)
+ server->current_session = NULL;
+ silc_dlist_del(server->ftp_sessions, ftp);
}
}
+
+ /* Save some transmission data */
+ if (offset && filesize) {
+ ftp->percent = ((double)offset / (double)filesize) * (double)100.0;
+ ftp->kps = (double)((offset / (double)(time(NULL) - ftp->starttime)) +
+ 1023) / (double)1024;
+ ftp->offset = offset;
+ ftp->filesize = filesize;
+ }
}
typedef struct {
{
SilcClientConnection conn;
SilcClientEntry *entrys, client_entry;
+ SilcClientFileError ret;
uint32 entry_count;
char *nickname = NULL, *tmp;
unsigned char **argv;
uint32 argc;
uint32 *argv_lens, *argv_types;
- int type;
+ int type = 0;
FtpSession ftp;
if (!server || !IS_SILC_SERVER(server) || !server->connected)
conn = server->conn;
/* Now parse all arguments */
- tmp = g_strconcat("KEY", " ", data, NULL);
+ tmp = g_strconcat("FILE", " ", data, NULL);
silc_parse_command_line(tmp, &argv, &argv_lens, &argv_types, &argc, 4);
g_free(tmp);
- if (argc < 2)
- cmd_return_error(CMDERR_NOT_ENOUGH_PARAMS);
-
- type = 0;
- if (!strcasecmp(argv[1], "send"))
- type = 1;
- if (!strcasecmp(argv[1], "receive"))
- type = 2;
- if (!strcasecmp(argv[1], "close"))
- type = 3;
- if (!strcasecmp(argv[1], "show"))
+ if (argc == 1)
type = 4;
+
+ if (argc >= 2) {
+ if (!strcasecmp(argv[1], "send"))
+ type = 1;
+ if (!strcasecmp(argv[1], "receive"))
+ type = 2;
+ if (!strcasecmp(argv[1], "close"))
+ type = 3;
+ }
if (type == 0)
cmd_return_error(CMDERR_NOT_ENOUGH_PARAMS);
goto out;
}
- if (!silc_client_file_receive(silc_client, conn,
- silc_client_file_monitor, server,
- server->current_session->client_entry,
- server->current_session->session_id))
- printformat_module("fe-common/silc", server, NULL,
- MSGLEVEL_CRAP, SILCTXT_FILE_CLIENT_NA,
- server->current_session->client_entry->nickname);
+ ret = silc_client_file_receive(silc_client, conn,
+ silc_client_file_monitor, server,
+ server->current_session->client_entry,
+ server->current_session->session_id);
+ if (ret != SILC_CLIENT_FILE_OK) {
+ if (ret == SILC_CLIENT_FILE_ALREADY_STARTED)
+ printformat_module("fe-common/silc", server, NULL,
+ MSGLEVEL_CRAP, SILCTXT_FILE_ALREADY_STARTED,
+ server->current_session->client_entry->nickname);
+ else
+ printformat_module("fe-common/silc", server, NULL,
+ MSGLEVEL_CRAP, SILCTXT_FILE_CLIENT_NA,
+ server->current_session->client_entry->nickname);
+ }
goto out;
}
silc_dlist_start(server->ftp_sessions);
while ((ftp = silc_dlist_get(server->ftp_sessions)) != SILC_LIST_END) {
if (ftp->client_entry == client_entry) {
- if (!silc_client_file_receive(silc_client, conn,
- silc_client_file_monitor, server,
- ftp->client_entry,
- ftp->session_id))
- printformat_module("fe-common/silc", server, NULL,
- MSGLEVEL_CRAP, SILCTXT_FILE_CLIENT_NA,
- server->current_session->client_entry->nickname);
+ ret = silc_client_file_receive(silc_client, conn,
+ silc_client_file_monitor, server,
+ ftp->client_entry,
+ ftp->session_id);
+ if (ret != SILC_CLIENT_FILE_OK) {
+ if (ret == SILC_CLIENT_FILE_ALREADY_STARTED)
+ printformat_module("fe-common/silc", server, NULL,
+ MSGLEVEL_CRAP, SILCTXT_FILE_ALREADY_STARTED,
+ client_entry->nickname);
+ else
+ printformat_module("fe-common/silc", server, NULL,
+ MSGLEVEL_CRAP, SILCTXT_FILE_CLIENT_NA,
+ client_entry->nickname);
+ }
break;
}
}
goto out;
}
- if (!silc_client_file_close(silc_client, conn,
- server->current_session->session_id))
+ ret = silc_client_file_close(silc_client, conn,
+ server->current_session->session_id);
+ if (ret != SILC_CLIENT_FILE_OK) {
+ if (ret == SILC_CLIENT_FILE_ALREADY_STARTED)
+ printformat_module("fe-common/silc", server, NULL,
+ MSGLEVEL_CRAP, SILCTXT_FILE_ALREADY_STARTED,
+ server->current_session->client_entry->nickname);
+ else
+ printformat_module("fe-common/silc", server, NULL,
+ MSGLEVEL_CRAP, SILCTXT_FILE_CLIENT_NA,
+ server->current_session->client_entry->nickname);
+ } else {
printformat_module("fe-common/silc", server, NULL,
- MSGLEVEL_CRAP, SILCTXT_FILE_CLIENT_NA,
+ MSGLEVEL_CRAP, SILCTXT_FILE_CLOSED,
server->current_session->client_entry->nickname);
-
+ }
goto out;
}
silc_dlist_start(server->ftp_sessions);
while ((ftp = silc_dlist_get(server->ftp_sessions)) != SILC_LIST_END) {
if (ftp->client_entry == client_entry) {
- if (!silc_client_file_close(silc_client, conn, ftp->session_id))
+ ret = silc_client_file_close(silc_client, conn, ftp->session_id);
+ if (ret != SILC_CLIENT_FILE_OK) {
+ if (ret == SILC_CLIENT_FILE_ALREADY_STARTED)
+ printformat_module("fe-common/silc", server, NULL,
+ MSGLEVEL_CRAP, SILCTXT_FILE_ALREADY_STARTED,
+ client_entry->nickname);
+ else
+ printformat_module("fe-common/silc", server, NULL,
+ MSGLEVEL_CRAP, SILCTXT_FILE_CLIENT_NA,
+ client_entry->nickname);
+ } else {
printformat_module("fe-common/silc", server, NULL,
- MSGLEVEL_CRAP, SILCTXT_FILE_CLIENT_NA,
- server->current_session->client_entry->nickname);
+ MSGLEVEL_CRAP, SILCTXT_FILE_CLOSED,
+ client_entry->nickname);
+ }
+
silc_dlist_del(server->ftp_sessions, ftp);
silc_free(ftp->filepath);
silc_free(ftp);
printformat_module("fe-common/silc", server, NULL,
MSGLEVEL_CRAP, SILCTXT_FILE_SHOW_LINE,
ftp->client_entry->nickname,
- ftp->filepath ? ftp->filepath : "[unknown]",
- ftp->send ? "send" : "receive");
+ ftp->send ? "send" : "receive",
+ (uint32)(ftp->offset + 1023) / 1024,
+ (uint32)(ftp->filesize + 1023) / 1024,
+ ftp->percent, ftp->kps,
+ ftp->filepath ? ftp->filepath : "[N/A]");
}
break;
SilcClientEntry client_entry;
SilcSocketConnection sock;
+ SilcBuffer packet;
char *hostname;
uint16 port;
int fd;
};
-void silc_client_ftp_free_sessions(SilcClient client,
- SilcClientConnection conn)
+SILC_TASK_CALLBACK(silc_client_ftp_connected)
{
- if (conn->ftp_sessions) {
- SilcClientFtpSession session;
- silc_dlist_start(conn->ftp_sessions);
- while ((session = silc_dlist_get(conn->ftp_sessions)) != SILC_LIST_END) {
- session->sock->user_data = NULL;
+ 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->ops->say(client, conn, SILC_CLIENT_MESSAGE_ERROR,
+ "Could not connect to client %s: %s",
+ ctx->host, strerror(opt));
+ client->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->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);
}
- silc_dlist_del(conn->ftp_sessions, session);
- silc_dlist_uninit(conn->ftp_sessions);
+ 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);
}
-void silc_client_ftp_session_free_client(SilcClientConnection conn,
- SilcClientEntry client_entry)
+static int
+silc_client_connect_to_client_internal(SilcClientInternalConnectContext *ctx)
{
- SilcClientFtpSession session;
+ int sock;
- if (!conn->ftp_sessions)
- return;
+ /* Create connection to server asynchronously */
+ sock = silc_net_create_connection_async(NULL, ctx->port, ctx->host);
+ if (sock < 0)
+ return -1;
- /* Get the session */
- silc_dlist_start(conn->ftp_sessions);
- while ((session = silc_dlist_get(conn->ftp_sessions)) != SILC_LIST_END) {
- if (session->client_entry == client_entry) {
- session->sock->user_data = NULL;
- silc_client_ftp_session_free(session);
- break;
- }
- }
+ /* 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);
+ ctx->sock = sock;
+ return sock;
}
-/* SFTP packet send callback */
+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(SilcSocketConnection sock,
SilcBuffer packet, void *context)
{
SilcClientFtpSession session = (SilcClientFtpSession)context;
SilcClient client = session->client;
- SilcBuffer buffer;
SILC_LOG_DEBUG(("Start"));
- buffer = silc_buffer_alloc(1 + packet->len);
- silc_buffer_pull_tail(buffer, SILC_BUFFER_END(buffer));
- silc_buffer_format(buffer,
+ /* 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, sock, SILC_PACKET_FTP, NULL, 0, NULL, NULL,
- buffer->data, buffer->len, TRUE);
+ session->packet->data, session->packet->len, TRUE);
- silc_buffer_free(buffer);
+ silc_buffer_clear(session->packet);
}
-/* SFTP monitor callback for SFTP server */
+/* SFTP monitor callback for SFTP server. This reports the application
+ how the transmission is going along. This function is for the client
+ who made the file available for download. */
static void silc_client_ftp_monitor(SilcSFTP sftp,
SilcSFTPMonitors type,
}
}
-/* Returns the read data */
+/* 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
+ the application. */
static void silc_client_ftp_data(SilcSFTP sftp,
SilcSFTPStatus status,
SILC_LOG_DEBUG(("Start"));
if (status == SILC_SFTP_STATUS_EOF) {
+ /* EOF received */
/* Close the handle */
silc_sftp_close(sftp, session->read_handle, NULL, NULL);
}
if (status != SILC_SFTP_STATUS_OK) {
- /* XXX errror */
+ /* Call monitor callback */
+ if (session->monitor)
+ (*session->monitor)(session->client, session->conn,
+ SILC_CLIENT_FILE_MONITOR_ERROR, 0, 0,
+ session->client_entry, session->session_id,
+ session->filepath, session->monitor_context);
/* Close the handle */
silc_sftp_close(sftp, session->read_handle, NULL, NULL);
/* Read more, until EOF is received */
session->read_offset += data_len;
- silc_sftp_read(sftp, session->read_handle, session->read_offset, 16384,
+ silc_sftp_read(sftp, session->read_handle, session->read_offset, 64512,
silc_client_ftp_data, session);
/* Call monitor callback */
session->client_entry, session->session_id,
session->filepath, session->monitor_context);
- /* Write the read data */
+ /* 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.
+ This will begin reading the data from the file. */
+
static void silc_client_ftp_open_handle(SilcSFTP sftp,
SilcSFTPStatus status,
SilcSFTPHandle handle,
SILC_LOG_DEBUG(("Start"));
if (status != SILC_SFTP_STATUS_OK) {
- /* XXX errror */
+ /* Call monitor callback */
+ if (session->monitor)
+ (*session->monitor)(session->client, session->conn,
+ SILC_CLIENT_FILE_MONITOR_ERROR, 0, 0,
+ session->client_entry, session->session_id,
+ session->filepath, session->monitor_context);
return;
}
/* Open the actual local file */
session->fd = silc_file_open(session->filepath, O_RDWR | O_CREAT);
if (session->fd < 0) {
- /* XXX errror */
+ /* Call monitor callback */
+ if (session->monitor)
+ (*session->monitor)(session->client, session->conn,
+ SILC_CLIENT_FILE_MONITOR_ERROR, 0, 0,
+ session->client_entry, session->session_id,
+ session->filepath, session->monitor_context);
return;
}
- session->read_handle = handle;
+ session->read_handle = handle;
/* Now, start reading the file */
- silc_sftp_read(sftp, session->read_handle, session->read_offset, 16384,
+ silc_sftp_read(sftp, session->read_handle, session->read_offset, 64512,
silc_client_ftp_data, session);
/* Call monitor callback */
session->filepath, session->monitor_context);
}
-/* Returns the file name available for download. */
+/* Returns the file name available for download. This is the downloader's
+ function. */
static void silc_client_ftp_readdir_name(SilcSFTP sftp,
SilcSFTPStatus status,
SILC_LOG_DEBUG(("Start"));
if (status != SILC_SFTP_STATUS_OK) {
- /* XXX errror */
+ /* Call monitor callback */
+ if (session->monitor)
+ (*session->monitor)(session->client, session->conn,
+ SILC_CLIENT_FILE_MONITOR_ERROR, 0, 0,
+ session->client_entry, session->session_id,
+ session->filepath, session->monitor_context);
+ return;
}
/* Now open the file */
session->dir_handle = NULL;
}
-/* Returns the file handle after giving opendir command. */
+/* Returns the file handle after giving opendir command. This is the
+ downloader's function. */
static void silc_client_ftp_opendir_handle(SilcSFTP sftp,
SilcSFTPStatus status,
SILC_LOG_DEBUG(("Start"));
if (status != SILC_SFTP_STATUS_OK) {
- /* XXX errror */
+ /* Call monitor callback */
+ if (session->monitor)
+ (*session->monitor)(session->client, session->conn,
+ SILC_CLIENT_FILE_MONITOR_ERROR, 0, 0,
+ session->client_entry, session->session_id,
+ session->filepath, session->monitor_context);
+ return;
}
/* Now, read the directory */
session->dir_handle = handle;
}
-/* SFTP version callback for SFTP client */
+/* SFTP version callback for SFTP client. This is the downloader's function
+ after initializing the SFTP connection to the remote client. This will
+ find out the filename available for download. */
static void silc_client_ftp_version(SilcSFTP sftp,
SilcSFTPStatus status,
SILC_LOG_DEBUG(("Start"));
if (status != SILC_SFTP_STATUS_OK) {
- /* XXX errror */
+ /* Call monitor callback */
+ if (session->monitor)
+ (*session->monitor)(session->client, session->conn,
+ SILC_CLIENT_FILE_MONITOR_ERROR, 0, 0,
+ session->client_entry, session->session_id,
+ session->filepath, session->monitor_context);
+ return;
}
/* The SFTP session is open, now retrieve the info about available file. */
silc_protocol_free(protocol);
}
+/* The downloader's function to start the key agreement protocol with the
+ remote client after we have connected to it. */
+
static void silc_client_ftp_start_key_agreement(SilcClientFtpSession session,
int sock)
{
SILC_LOG_DEBUG(("Start"));
+ /* Call monitor callback */
+ if (session->monitor)
+ (*session->monitor)(session->client, session->conn,
+ SILC_CLIENT_FILE_MONITOR_KEY_AGREEMENT, 0, 0,
+ session->client_entry, session->session_id,
+ NULL, session->monitor_context);
+
/* Add new connection for this session */
conn = silc_client_add_connection(client, session->hostname,
session->port, session);
silc_protocol_execute(protocol, client->schedule, 0, 0);
}
-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->ops->say(client, conn, SILC_CLIENT_MESSAGE_ERROR,
- "Could not connect to client %s: %s",
- ctx->host, strerror(opt));
- client->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->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);
-
- 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);
-}
-
-/* Free session */
-
-void silc_client_ftp_session_free(SilcClientFtpSession session)
-{
- SilcClientConnection conn;
-
- silc_dlist_del(session->conn->ftp_sessions, session);
-
- 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);
-
- if (session->listener) {
- silc_schedule_unset_listen_fd(session->client->schedule,
- session->listener);
- silc_net_close_connection(session->listener);
- }
-
- 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->active_session == session)
- conn->active_session = NULL;
-
- silc_client_close_connection(session->client, session->sock, conn);
- } else {
- silc_socket_free(session->sock);
- }
- }
-
- silc_free(session->hostname);
- silc_free(session->filepath);
- silc_free(session);
-}
+/* 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. */
SILC_TASK_CALLBACK(silc_client_ftp_process_key_agreement)
{
sock = silc_net_accept_connection(session->listener);
if (sock < 0) {
- /* XXX error */
+ /* Call monitor callback */
+ if (session->monitor)
+ (*session->monitor)(session->client, session->conn,
+ SILC_CLIENT_FILE_MONITOR_ERROR, 0, 0,
+ session->client_entry, session->session_id,
+ session->filepath, session->monitor_context);
return;
}
/* 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) {
- /* XXX error */
+ /* Call monitor callback */
+ if (session->monitor)
+ (*session->monitor)(session->client, session->conn,
+ SILC_CLIENT_FILE_MONITOR_ERROR, 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, 0, 0,
+ session->client_entry, session->session_id,
+ NULL, session->monitor_context);
+
/* Add new connection for this session */
conn = silc_client_add_connection(client, newsocket->hostname,
newsocket->port, session);
SILC_CLIENT_REGISTER_CONNECTION_FOR_IO(sock);
}
+/* Free all file transfer sessions. */
+
+void silc_client_ftp_free_sessions(SilcClient client,
+ SilcClientConnection conn)
+{
+ if (conn->ftp_sessions) {
+ SilcClientFtpSession session;
+ silc_dlist_start(conn->ftp_sessions);
+ while ((session = silc_dlist_get(conn->ftp_sessions)) != SILC_LIST_END) {
+ session->sock->user_data = NULL;
+ silc_client_ftp_session_free(session);
+ }
+ silc_dlist_del(conn->ftp_sessions, session);
+ silc_dlist_uninit(conn->ftp_sessions);
+ }
+}
+
+/* Free file transfer session by client entry. */
+
+void silc_client_ftp_session_free_client(SilcClientConnection conn,
+ SilcClientEntry client_entry)
+{
+ SilcClientFtpSession session;
+
+ if (!conn->ftp_sessions)
+ return;
+
+ /* Get the session */
+ silc_dlist_start(conn->ftp_sessions);
+ while ((session = silc_dlist_get(conn->ftp_sessions)) != SILC_LIST_END) {
+ if (session->client_entry == client_entry) {
+ session->sock->user_data = NULL;
+ silc_client_ftp_session_free(session);
+ break;
+ }
+ }
+}
+
+/* Free session resources. */
+
+void silc_client_ftp_session_free(SilcClientFtpSession session)
+{
+ SilcClientConnection conn;
+
+ SILC_LOG_DEBUG(("Free session"));
+
+ silc_dlist_del(session->conn->ftp_sessions, session);
+
+ 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->active_session == session)
+ conn->active_session = NULL;
+
+ silc_client_close_connection(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);
+}
+
+/* Sends a file indicated by the `filepath' to the remote client
+ indicated by the `client_entry'. This will negotiate a secret key
+ with the remote client before actually starting the transmission of
+ the file. The `monitor' callback will be called to monitor the
+ transmission of the file.
+
+ This returns a file session ID for the file transmission. It can
+ be used to close the session (and abort the file transmission) by
+ calling the silc_client_file_close function. The session ID is
+ also returned in the `monitor' callback. This returns 0 if the
+ file indicated by the `filepath' is being transmitted to the remote
+ client indicated by the `client_entry', already. */
+
uint32 silc_client_file_send(SilcClient client,
SilcClientConnection conn,
SilcClientFileMonitor monitor,
/* Add new session */
session = silc_calloc(1, sizeof(*session));
- session->session_id = conn->next_session_id++;
+ session->session_id = ++conn->next_session_id;
session->client = client;
session->conn = conn;
session->client_entry = client_entry;
return session->session_id;
}
-bool silc_client_file_receive(SilcClient client,
- SilcClientConnection conn,
- SilcClientFileMonitor monitor,
- void *monitor_context,
- SilcClientEntry client_entry,
- uint32 session_id)
+/* Receives a file from a client indicated by the `client_entry'. The
+ `session_id' indicates the file transmission session and it has been
+ received in the `ftp' client operation function. This will actually
+ perform the key agreement protocol with the remote client before
+ actually starting the file transmission. The `monitor' callback
+ will be called to monitor the transmission. */
+
+SilcClientFileError
+silc_client_file_receive(SilcClient client,
+ SilcClientConnection conn,
+ SilcClientFileMonitor monitor,
+ void *monitor_context,
+ SilcClientEntry client_entry,
+ uint32 session_id)
{
SilcClientFtpSession session;
SilcBuffer keyagr, ftp;
if (session == SILC_LIST_END) {
SILC_LOG_DEBUG(("Unknown session ID: %d\n", session_id));
- return FALSE;
+ return SILC_CLIENT_FILE_UNKNOWN_SESSION;
}
/* See if we have this session running already */
if (session->sftp || session->listener) {
SILC_LOG_DEBUG(("Session already started"));
- return FALSE;
+ return SILC_CLIENT_FILE_ALREADY_STARTED;
}
session->monitor = monitor;
session->client_entry = client_entry;
session->conn = conn;
- /* Add the listener for the key agreement */
- session->hostname = silc_net_localip();
- session->listener = silc_net_create_server(0, session->hostname);
- if (session->listener < 0) {
- /* XXX Error */
- SILC_LOG_DEBUG(("Could not create listener"));
- return FALSE;
+ /* If the hostname and port already exists then the remote client did
+ provide the connection point to us and we won't create listener, but
+ create the connection ourselves. */
+ if (session->hostname && session->port) {
+ if (silc_client_connect_to_client(client, conn, session->port,
+ session->hostname, session) < 0)
+ return SILC_CLIENT_FILE_ERROR;
+ } else {
+ /* Add the listener for the key agreement */
+ session->hostname = silc_net_localip();
+ session->listener = silc_net_create_server(0, session->hostname);
+ if (session->listener < 0) {
+ SILC_LOG_DEBUG(("Could not create listener"));
+ 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);
+
+ /* 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);
+
+ silc_buffer_free(keyagr);
+ silc_buffer_free(ftp);
}
- 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);
-
- /* 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);
-
- silc_buffer_free(keyagr);
- silc_buffer_free(ftp);
-
- return TRUE;
+ return SILC_CLIENT_FILE_OK;
}
-/* Closes FTP session */
+/* 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
+ after successful file transmission. This function can be used
+ also to reject incoming file transmission request. */
-bool silc_client_file_close(SilcClient client,
- SilcClientConnection conn,
- uint32 session_id)
+SilcClientFileError silc_client_file_close(SilcClient client,
+ SilcClientConnection conn,
+ uint32 session_id)
{
SilcClientFtpSession session;
if (session == SILC_LIST_END) {
SILC_LOG_DEBUG(("Unknown session ID: %d\n", session_id));
- return FALSE;
+ return SILC_CLIENT_FILE_UNKNOWN_SESSION;
}
silc_client_ftp_session_free(session);
- return TRUE;
+ return SILC_CLIENT_FILE_OK;
}
/* Callback called after remote client information has been resolved.
then continue with the key agreement protocol. If not then it means
this is a file transfer request and we let the application know. */
-static void
-silc_client_ftp_resolve_cb(SilcClient client,
- SilcClientConnection conn,
- SilcClientEntry *clients,
- uint32 clients_count,
- void *context)
+static void silc_client_ftp_resolve_cb(SilcClient client,
+ SilcClientConnection conn,
+ SilcClientEntry *clients,
+ uint32 clients_count,
+ void *context)
{
SilcPacketContext *packet = (SilcPacketContext *)context;
SilcClientFtpSession session;
- SilcKeyAgreementPayload payload;
+ SilcKeyAgreementPayload payload = NULL;
SilcClientEntry client_entry;
char *hostname;
uint16 port;
- int sock;
SILC_LOG_DEBUG(("Start"));
if (session == SILC_LIST_END) {
/* No session found, create one and let the application know about
- incomoing file transfer request. */
+ incoming file transfer request. */
/* Add new session */
session = silc_calloc(1, sizeof(*session));
- session->session_id = conn->next_session_id++;
+ session->session_id = ++conn->next_session_id;
session->client = client;
session->conn = conn;
silc_dlist_add(conn->ftp_sessions, session);
client->ops->ftp(client, conn, client_entry,
session->session_id, hostname, port);
- /* If hostname was provided we'll start the key exchange now. */
if (hostname && port) {
- /* XXX */
+ session->hostname = strdup(hostname);
+ session->port = port;
}
-
- silc_key_agreement_payload_free(payload);
+
goto out;
}
session->port = port;
/* Session exists, continue with key agreement protocol. */
- sock = silc_client_connect_to_client(client, conn, port, hostname,
- session);
- if (sock < 0)
- goto out;
+ if (silc_client_connect_to_client(client, conn, port,
+ hostname, session) < 0) {
+ /* Call monitor callback */
+ if (session->monitor)
+ (*session->monitor)(session->client, session->conn,
+ SILC_CLIENT_FILE_MONITOR_ERROR, 0, 0,
+ session->client_entry, session->session_id,
+ session->filepath, session->monitor_context);
+ }
out:
+ if (payload)
+ silc_key_agreement_payload_free(payload);
silc_packet_context_free(packet);
}
silc_buffer_pull(packet->buffer, 1);
- /* If we have active FTP session then give the packet to the
+ /* If we have active FTP session then give the packet directly to the
protocol processor. */
if (conn->active_session) {
/* Give it to the SFTP */