X-Git-Url: http://git.silcnet.org/gitweb/?a=blobdiff_plain;f=lib%2Fsilcsftp%2Fsftp_client.c;h=10eb160cbcbdf0ee7839960655a92197c0721ec6;hb=HEAD;hp=1b198b860f689488be8b7544deb01fb1f37d786c;hpb=c257b555225193e54d85daf541d29578b3c93882;p=silc.git diff --git a/lib/silcsftp/sftp_client.c b/lib/silcsftp/sftp_client.c index 1b198b86..10eb160c 100644 --- a/lib/silcsftp/sftp_client.c +++ b/lib/silcsftp/sftp_client.c @@ -4,7 +4,7 @@ Author: Pekka Riikonen - Copyright (C) 2001 2003 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 @@ -18,15 +18,14 @@ */ /* $Id$ */ -#include "silcincludes.h" +#include "silc.h" #include "silcsftp.h" #include "sftp_util.h" /* 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; @@ -34,18 +33,20 @@ typedef struct SilcSFTPRequestStruct { SilcSFTPAttrCallback attr; SilcSFTPExtendedCallback extended; void *context; - struct SilcSFTPRequestStruct *next; + SilcUInt32 id; + SilcSFTPPacket type; } *SilcSFTPRequest; /* SFTP client context */ typedef struct { - SilcSFTPSendPacketCallback send_packet; - void *send_context; + SilcStream stream; + SilcSchedule schedule; SilcSFTPVersionCallback version; - void *version_context; - SilcUInt32 id; + SilcSFTPErrorCallback error; + void *context; SilcList requests; SilcBuffer packet; + SilcUInt32 id; } *SilcSFTPClient; /* File handle */ @@ -54,6 +55,9 @@ struct SilcSFTPHandleStruct { SilcUInt32 data_len; }; +static void silc_sftp_client_receive_process(SilcSFTP context, + SilcBuffer buffer); + /* Creates SilcSFTPHandle and returns pointer to it. The caller must free the context. */ @@ -92,7 +96,7 @@ static void silc_sftp_handle_get(SilcSFTPHandle handle, *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, @@ -100,6 +104,7 @@ static void silc_sftp_send_packet(SilcSFTPClient sftp, { SilcBuffer tmp; va_list vp; + int ret; va_start(vp, len); tmp = silc_sftp_packet_encode_vp(type, sftp->packet, len, vp); @@ -109,19 +114,38 @@ static void silc_sftp_send_packet(SilcSFTPClient sftp, sftp->packet = tmp; SILC_LOG_HEXDUMP(("SFTP packet to server"), sftp->packet->data, - sftp->packet->len); + silc_buffer_len(sftp->packet)); /* Send the packet */ - (*sftp->send_packet)(sftp->packet, sftp->send_context); + 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 */ - sftp->packet->data = sftp->packet->tail = sftp->packet->head; - sftp->packet->len = 0; + silc_buffer_reset(sftp->packet); } /* Finds request by request ID. */ -static SilcSFTPRequest silc_sftp_find_request(SilcSFTPClient sftp, SilcUInt32 id) +static SilcSFTPRequest silc_sftp_find_request(SilcSFTPClient sftp, + SilcUInt32 id) { SilcSFTPRequest req; @@ -133,7 +157,7 @@ static SilcSFTPRequest silc_sftp_find_request(SilcSFTPClient sftp, SilcUInt32 id return req; } - SILC_LOG_DEBUG(("Unknown request ID")); + SILC_LOG_DEBUG(("Unknown request ID %d", id)); return NULL; } @@ -289,6 +313,7 @@ static void silc_sftp_call_request(SilcSFTPClient sftp, break; default: + SILC_LOG_DEBUG(("Unknown request type %d", req->type)); break; } @@ -299,31 +324,104 @@ static void silc_sftp_call_request(SilcSFTPClient sftp, va_end(vp); } -/* Starts SFTP client and returns context for it. The version callback - indicated by the `callback' will be called after the SFTP session has - been started and server has returned the version of the protocol. The - SFTP client context is returned in the callback too. This returns - allocated SFTP client context or NULL on error. */ +/* 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)) + return; + + SILC_LOG_DEBUG(("Writing pending data to stream")); + + /* Write pending data to stream */ + silc_buffer_push(sftp->packet, silc_buffer_headlen(sftp->packet)); + while (silc_buffer_len(sftp->packet) > 0) { + ret = silc_stream_write(stream, sftp->packet->data, + silc_buffer_len(sftp->packet)); + if (ret == 0) { + sftp->error(context, SILC_SFTP_STATUS_EOF, sftp->context); + silc_buffer_reset(sftp->packet); + return; + } + + if (ret == -2) { + sftp->error(context, SILC_SFTP_STATUS_NO_CONNECTION, sftp->context); + silc_buffer_reset(sftp->packet); + return; + } + + if (ret == -1) + return; + + /* Wrote data */ + silc_buffer_pull(sftp->packet, ret); + } + break; + + default: + break; + } +} + +/* Starts SFTP client and returns context for it. */ -SilcSFTP silc_sftp_client_start(SilcSFTPSendPacketCallback send_packet, - void *send_context, - SilcSFTPVersionCallback callback, +SilcSFTP silc_sftp_client_start(SilcStream stream, + SilcSchedule schedule, + SilcSFTPVersionCallback version_cb, + SilcSFTPErrorCallback error_cb, void *context) { SilcSFTPClient sftp; - if (!send_packet) + SILC_LOG_DEBUG(("Starting SFTP client")); + + if (!schedule) + schedule = silc_schedule_get_global(); + + if (!stream) return NULL; sftp = silc_calloc(1, sizeof(*sftp)); if (!sftp) return NULL; - sftp->send_packet = send_packet; - sftp->send_context = send_context; - sftp->version = callback; - sftp->version_context = context; + sftp->stream = stream; + 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, schedule, silc_sftp_client_io, sftp); + /* Send the SFTP session initialization to the server */ silc_sftp_send_packet(sftp, SILC_SFTP_INIT, 4, SILC_STR_UI_INT(SILC_SFTP_PROTOCOL_VERSION), @@ -340,39 +438,60 @@ void silc_sftp_client_shutdown(SilcSFTP context) { 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); } /* Function that is called to process the incmoing SFTP packet. */ -/* XXX Some day this will go away and we have automatic receive callbacks - for SilcSocketConnection API or SilcPacketContext API. */ -void silc_sftp_client_receive_process(SilcSFTP context, - SilcSocketConnection sock, - SilcPacketContext *packet) +void silc_sftp_client_receive_process(SilcSFTP context, SilcBuffer buffer) { 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; @@ -384,13 +503,13 @@ void silc_sftp_client_receive_process(SilcSFTP context, 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; @@ -462,31 +581,6 @@ void silc_sftp_client_receive_process(SilcSFTP context, } 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; @@ -526,10 +620,11 @@ void silc_sftp_client_receive_process(SilcSFTP context, SILC_LOG_DEBUG(("Attributes packet")); - ret = silc_buffer_unformat(&buf, - SILC_STR_UI_INT(&id), - SILC_STR_UI_XNSTRING(&data, buf.len - 4), - SILC_STR_END); + ret = + silc_buffer_unformat(&buf, + SILC_STR_UI_INT(&id), + SILC_STR_DATA(&data, silc_buffer_len(&buf) - 4), + SILC_STR_END); if (ret < 0) break; @@ -538,7 +633,7 @@ void silc_sftp_client_receive_process(SilcSFTP context, if (!req) break; - silc_buffer_set(&tmpbuf, data, buf.len - 4); + silc_buffer_set(&tmpbuf, data, silc_buffer_len(&buf) - 4); attr = silc_sftp_attr_decode(&tmpbuf); if (!attr) break; @@ -556,7 +651,8 @@ void silc_sftp_client_receive_process(SilcSFTP context, ret = silc_buffer_unformat(&buf, SILC_STR_UI_INT(&id), - SILC_STR_UI_XNSTRING(&data, buf.len - 4), + SILC_STR_DATA(&data, + silc_buffer_len(&buf) - 4), SILC_STR_END); if (ret < 0) break; @@ -568,7 +664,7 @@ void silc_sftp_client_receive_process(SilcSFTP context, /* Call the callback */ silc_sftp_call_request(sftp, req, type, SILC_SFTP_STATUS_OK, - data, buf.len - 4); + data, silc_buffer_len(&buf) - 4); } break; @@ -603,7 +699,7 @@ void silc_sftp_open(SilcSFTP sftp, attrs_buf = silc_sftp_attr_encode(attrs); if (!attrs_buf) return; - len = 4 + 4 + strlen(filename) + 4 + attrs_buf->len; + len = 4 + 4 + strlen(filename) + 4 + silc_buffer_len(attrs_buf); silc_sftp_send_packet(client, req->type, len, SILC_STR_UI_INT(req->id), @@ -611,7 +707,7 @@ void silc_sftp_open(SilcSFTP sftp, SILC_STR_UI32_STRING(filename), SILC_STR_UI_INT(pflags), SILC_STR_UI_XNSTRING(attrs_buf->data, - attrs_buf->len), + silc_buffer_len(attrs_buf)), SILC_STR_END); silc_buffer_free(attrs_buf); @@ -810,14 +906,14 @@ void silc_sftp_mkdir(SilcSFTP sftp, attrs_buf = silc_sftp_attr_encode(attrs); if (!attrs_buf) return; - len = 4 + 4 + strlen(path) + attrs_buf->len; + len = 4 + 4 + strlen(path) + silc_buffer_len(attrs_buf); silc_sftp_send_packet(client, req->type, len, SILC_STR_UI_INT(req->id), SILC_STR_UI_INT(strlen(path)), SILC_STR_UI32_STRING(path), SILC_STR_UI_XNSTRING(attrs_buf->data, - attrs_buf->len), + silc_buffer_len(attrs_buf)), SILC_STR_END); silc_buffer_free(attrs_buf); @@ -1028,14 +1124,14 @@ void silc_sftp_setstat(SilcSFTP sftp, attrs_buf = silc_sftp_attr_encode(attrs); if (!attrs_buf) return; - len = 4 + 4 + strlen(path) + attrs_buf->len; + len = 4 + 4 + strlen(path) + silc_buffer_len(attrs_buf); silc_sftp_send_packet(client, req->type, len, SILC_STR_UI_INT(req->id), SILC_STR_UI_INT(strlen(path)), SILC_STR_UI32_STRING(path), SILC_STR_UI_XNSTRING(attrs_buf->data, - attrs_buf->len), + silc_buffer_len(attrs_buf)), SILC_STR_END); silc_buffer_free(attrs_buf); @@ -1069,14 +1165,14 @@ void silc_sftp_fsetstat(SilcSFTP sftp, attrs_buf = silc_sftp_attr_encode(attrs); if (!attrs_buf) return; - len = 4 + 4 + hdata_len + attrs_buf->len; + len = 4 + 4 + hdata_len + silc_buffer_len(attrs_buf); silc_sftp_send_packet(client, req->type, len, SILC_STR_UI_INT(req->id), SILC_STR_UI_INT(hdata_len), SILC_STR_UI_XNSTRING(hdata, hdata_len), SILC_STR_UI_XNSTRING(attrs_buf->data, - attrs_buf->len), + silc_buffer_len(attrs_buf)), SILC_STR_END); silc_buffer_free(attrs_buf);