Merged silc_1_1_branch to trunk.
[silc.git] / lib / silcsftp / sftp_client.c
index 1b198b860f689488be8b7544deb01fb1f37d786c..59f7dfb61a29ce9ad9d9d8df216d3a45c31967df 100644 (file)
@@ -4,7 +4,7 @@
 
   Author: Pekka Riikonen <priikone@silcnet.org>
 
-  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
 */
 /* $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,101 @@ 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 */
 
-SilcSFTP silc_sftp_client_start(SilcSFTPSendPacketCallback send_packet,
-                               void *send_context,
-                               SilcSFTPVersionCallback callback,
+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(SilcStream stream,
+                               SilcSchedule schedule,
+                               SilcSFTPVersionCallback version_cb,
+                               SilcSFTPErrorCallback error_cb,
                                void *context)
 {
   SilcSFTPClient sftp;
 
-  if (!send_packet)
+  SILC_LOG_DEBUG(("Starting SFTP client"));
+
+  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 +435,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 +500,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 +578,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 +617,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 +630,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 +648,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 +661,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 +696,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 +704,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 +903,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 +1121,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 +1162,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);