Author: Pekka Riikonen <priikone@silcnet.org>
- Copyright (C) 2001 - 2005 Pekka Riikonen
+ Copyright (C) 2001 - 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
/* Request context. Every request will allocate this context and set
the correct callback function according the `type' field. */
typedef struct SilcSFTPRequestStruct {
- SilcUInt32 id;
- SilcSFTPPacket type;
+ struct SilcSFTPRequestStruct *next;
SilcSFTPStatusCallback status;
SilcSFTPHandleCallback handle;
SilcSFTPDataCallback data;
SilcSFTPAttrCallback attr;
SilcSFTPExtendedCallback extended;
void *context;
- struct SilcSFTPRequestStruct *next;
+ SilcUInt32 id;
+ SilcSFTPPacket type;
} *SilcSFTPRequest;
/* SFTP client context */
typedef struct {
SilcStream stream;
+ SilcSchedule schedule;
SilcSFTPVersionCallback version;
- void *version_context;
- SilcUInt32 id;
+ SilcSFTPErrorCallback error;
+ void *context;
SilcList requests;
SilcBuffer packet;
+ SilcUInt32 id;
} *SilcSFTPClient;
/* File handle */
};
static void silc_sftp_client_receive_process(SilcSFTP context,
- SilcBuffer buffer)
+ SilcBuffer buffer);
/* Creates SilcSFTPHandle and returns pointer to it. The caller must free
the context. */
*data_len = handle->data_len;
}
-/* General routine to send SFTP packet to the SFTP server. */
+/* Generic routine to send SFTP packet to the SFTP server. */
static void silc_sftp_send_packet(SilcSFTPClient sftp,
SilcSFTPPacket type,
{
SilcBuffer tmp;
va_list vp;
+ int ret;
va_start(vp, len);
tmp = silc_sftp_packet_encode_vp(type, sftp->packet, len, vp);
silc_buffer_len(sftp->packet));
/* Send the packet */
- silc_stream_write(sftp->stream, sftp->packet->data,
- silc_buffer_len(sftp->packet));
+ while (silc_buffer_len(sftp->packet) > 0) {
+ ret = silc_stream_write(sftp->stream, silc_buffer_data(sftp->packet),
+ silc_buffer_len(sftp->packet));
+ if (ret == -2) {
+ SILC_LOG_ERROR(("Error sending SFTP packet type %d", type));
+ sftp->error((SilcSFTP)sftp, SILC_SFTP_STATUS_NO_CONNECTION,
+ sftp->context);
+ silc_buffer_reset(sftp->packet);
+ return;
+ }
+ if (ret == 0) {
+ sftp->error((SilcSFTP)sftp, SILC_SFTP_STATUS_EOF, sftp->context);
+ silc_buffer_reset(sftp->packet);
+ return;
+ }
+ if (ret == -1)
+ return;
+
+ silc_buffer_pull(sftp->packet, ret);
+ }
/* Clear packet */
silc_buffer_reset(sftp->packet);
return req;
}
- SILC_LOG_DEBUG(("Unknown request ID"));
+ SILC_LOG_DEBUG(("Unknown request ID %d", id));
return NULL;
}
break;
default:
+ SILC_LOG_DEBUG(("Unknown request type %d", req->type));
break;
}
va_end(vp);
}
-/* Handles stream IO */
+/* Handles stream I/O */
static void silc_sftp_client_io(SilcStream stream, SilcStreamStatus status,
void *context)
{
SilcSFTPClient sftp = context;
+ unsigned char inbuf[65536];
+ SilcBufferStruct packet;
+ int ret;
switch (status) {
+ case SILC_STREAM_CAN_READ:
+ SILC_LOG_DEBUG(("Reading data from stream"));
+
+ /* Read data from stream */
+ ret = silc_stream_read(stream, inbuf, sizeof(inbuf));
+ if (ret <= 0) {
+ if (ret == 0)
+ sftp->error(context, SILC_SFTP_STATUS_EOF, sftp->context);
+ if (ret == -2)
+ sftp->error(context, SILC_SFTP_STATUS_NO_CONNECTION, sftp->context);
+ return;
+ }
+
+ SILC_LOG_DEBUG(("Read %d bytes", ret));
+
+ /* Now process the SFTP packet */
+ silc_buffer_set(&packet, inbuf, ret);
+ silc_sftp_client_receive_process(context, &packet);
+ break;
case SILC_STREAM_CAN_WRITE:
- if (!silc_buffer_headlen(&sftp->packet))
+ if (!silc_buffer_headlen(sftp->packet))
return;
SILC_LOG_DEBUG(("Writing pending data to stream"));
ret = silc_stream_write(stream, sftp->packet->data,
silc_buffer_len(sftp->packet));
if (ret == 0) {
- /* EOS */
- /* XXX */
+ sftp->error(context, SILC_SFTP_STATUS_EOF, sftp->context);
silc_buffer_reset(sftp->packet);
return;
}
- if (i == -2) {
- /* Error */
- /* XXX */
+ if (ret == -2) {
+ sftp->error(context, SILC_SFTP_STATUS_NO_CONNECTION, sftp->context);
silc_buffer_reset(sftp->packet);
- return FALSE;
+ return;
}
- if (ret == -1) {
- /* Cannot write now, write later. */
- silc_buffer_pull(sftp->packet, silc_buffer_len(sftp->packet));
+ if (ret == -1)
return;
- }
/* Wrote data */
silc_buffer_pull(sftp->packet, ret);
}
break;
- case SILC_STREAM_CAN_READ:
- SILC_LOG_DEBUG(("Reading data from stream"));
-
- /* Make sure we have fair amount of free space in inbuf */
- if (silc_buffer_taillen(&ps->inbuf) < SILC_PACKET_DEFAULT_SIZE)
- if (!silc_buffer_realloc(&ps->inbuf, silc_buffer_truelen(&ps->inbuf) +
- SILC_PACKET_DEFAULT_SIZE * 2))
- return;
-
- /* Read data from stream */
- ret = silc_stream_read(ps->stream, &ps->inbuf.tail,
- silc_buffer_taillen(&ps->inbuf));
-
- if (ret == 0) {
- /* EOS */
- SILC_PACKET_CALLBACK_EOS(ps);
- silc_buffer_reset(&ps->inbuf);
- return;
- }
-
- if (ret == -2) {
- /* Error */
- SILC_PACKET_CALLBACK_ERROR(ps, SILC_PACKET_ERR_READ);
- silc_buffer_reset(&ps->inbuf);
- return;
- }
-
- if (ret == -1) {
- /* Cannot read now, do it later. */
- silc_buffer_pull(&ps->inbuf, silc_buffer_len(&ps->inbuf));
- return;
- }
-
- /* Read some data */
- silc_buffer_pull_tail(&ps->inbuf, ret);
-
- /* Now process the data */
- silc_sftp_client_receive_process(sftp);
- break;
-
default:
break;
}
/* Starts SFTP client and returns context for it. */
SilcSFTP silc_sftp_client_start(SilcStream stream,
- SilcSFTPVersionCallback callback,
+ SilcSchedule schedule,
+ SilcSFTPVersionCallback version_cb,
+ SilcSFTPErrorCallback error_cb,
void *context)
{
SilcSFTPClient sftp;
+ SILC_LOG_DEBUG(("Starting SFTP client"));
+
if (!stream)
return NULL;
if (!sftp)
return NULL;
sftp->stream = stream;
- sftp->version = callback;
- sftp->version_context = context;
+ sftp->version = version_cb;
+ sftp->error = error_cb;
+ sftp->context = context;
+ sftp->schedule = schedule;
silc_list_init(sftp->requests, struct SilcSFTPRequestStruct, next);
/* We handle the stream now */
- silc_stream_set_notifier(stream, silc_sftp_client_io, sftp);
+ silc_stream_set_notifier(stream, schedule, silc_sftp_client_io, sftp);
/* Send the SFTP session initialization to the server */
silc_sftp_send_packet(sftp, SILC_SFTP_INIT, 4,
{
SilcSFTPClient sftp = (SilcSFTPClient)context;
+ silc_stream_set_notifier(sftp->stream, sftp->schedule, NULL, NULL);
if (sftp->packet)
silc_buffer_free(sftp->packet);
silc_free(sftp);
SilcSFTPClient sftp = (SilcSFTPClient)context;
SilcSFTPRequest req;
SilcSFTPPacket type;
- const unsigned char *payload = NULL;
+ unsigned char *payload = NULL;
SilcUInt32 payload_len;
int ret;
SilcBufferStruct buf;
SilcUInt32 id;
- SILC_LOG_DEBUG(("Start"));
+ SILC_LOG_DEBUG(("Process SFTP packet"));
/* Parse the packet */
- type = silc_sftp_packet_decode(packet->buffer, (unsigned char **)&payload,
- &payload_len);
- if (!type)
+ type = silc_sftp_packet_decode(buffer, &payload, &payload_len);
+ if (type <= 0)
return;
- silc_buffer_set(&buf, (unsigned char *)payload, payload_len);
+ silc_buffer_set(&buf, payload, payload_len);
switch (type) {
+ case SILC_SFTP_DATA:
+ {
+ unsigned char *data = NULL;
+ SilcUInt32 data_len = 0;
+
+ SILC_LOG_DEBUG(("Data packet"));
+
+ ret = silc_buffer_unformat(&buf,
+ SILC_STR_UI_INT(&id),
+ SILC_STR_UI32_NSTRING(&data, &data_len),
+ SILC_STR_END);
+ if (ret < 0)
+ break;
+
+ /* Get request */
+ req = silc_sftp_find_request(sftp, id);
+ if (!req)
+ break;
+
+ /* Call the callback */
+ silc_sftp_call_request(sftp, req, type, SILC_SFTP_STATUS_OK,
+ data, data_len);
+ }
+ break;
+
case SILC_SFTP_VERSION:
{
SilcSFTPVersion version;
SILC_STR_END);
if (ret < 0) {
(*sftp->version)((SilcSFTP)sftp, SILC_SFTP_STATUS_FAILURE, 0,
- sftp->version_context);
+ sftp->context);
break;
}
/* Call the callback */
(*sftp->version)((SilcSFTP)sftp, SILC_SFTP_STATUS_OK, version,
- sftp->version_context);
+ sftp->context);
}
break;
}
break;
- case SILC_SFTP_DATA:
- {
- unsigned char *data = NULL;
- SilcUInt32 data_len = 0;
-
- SILC_LOG_DEBUG(("Data packet"));
-
- ret = silc_buffer_unformat(&buf,
- SILC_STR_UI_INT(&id),
- SILC_STR_UI32_NSTRING(&data, &data_len),
- SILC_STR_END);
- if (ret < 0)
- break;
-
- /* Get request */
- req = silc_sftp_find_request(sftp, id);
- if (!req)
- break;
-
- /* Call the callback */
- silc_sftp_call_request(sftp, req, type, SILC_SFTP_STATUS_OK,
- data, data_len);
- }
- break;
-
case SILC_SFTP_NAME:
{
SilcUInt32 count;
ret =
silc_buffer_unformat(&buf,
SILC_STR_UI_INT(&id),
- SILC_STR_UI_XNSTRING(&data,
- silc_buffer_len(&buf) - 4),
+ SILC_STR_DATA(&data, silc_buffer_len(&buf) - 4),
SILC_STR_END);
if (ret < 0)
break;
ret = silc_buffer_unformat(&buf,
SILC_STR_UI_INT(&id),
- SILC_STR_UI_XNSTRING(&data, silc_buffer_len(&buf) - 4),
+ SILC_STR_DATA(&data,
+ silc_buffer_len(&buf) - 4),
SILC_STR_END);
if (ret < 0)
break;